Merge branch 'master' into fix-fmt-compquat-sign

This commit is contained in:
Jeroen van Rijn
2024-06-04 13:25:57 +02:00
committed by GitHub
110 changed files with 6413 additions and 6232 deletions
+23
View File
@@ -34,6 +34,7 @@ jobs:
(cd tests/core; gmake all_bsd) (cd tests/core; gmake all_bsd)
(cd tests/internal; gmake all_bsd) (cd tests/internal; gmake all_bsd)
(cd tests/issues; ./run.sh) (cd tests/issues; ./run.sh)
(cd tests/benchmark; gmake all)
build_linux: build_linux:
name: Ubuntu Build, Check, and Test name: Ubuntu Build, Check, and Test
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -80,6 +81,11 @@ jobs:
cd tests/internal cd tests/internal
make make
timeout-minutes: 10 timeout-minutes: 10
- name: Odin core library benchmarks
run: |
cd tests/benchmark
make
timeout-minutes: 10
- name: Odin check examples/all for Linux i386 - name: Odin check examples/all for Linux i386
run: ./odin check examples/all -vet -strict-style -target:linux_i386 run: ./odin check examples/all -vet -strict-style -target:linux_i386
timeout-minutes: 10 timeout-minutes: 10
@@ -131,6 +137,11 @@ jobs:
cd tests/internal cd tests/internal
make make
timeout-minutes: 10 timeout-minutes: 10
- name: Odin core library benchmarks
run: |
cd tests/benchmark
make
timeout-minutes: 10
build_macOS_arm: build_macOS_arm:
name: MacOS ARM Build, Check, and Test name: MacOS ARM Build, Check, and Test
runs-on: macos-14 # This is an arm/m1 runner. runs-on: macos-14 # This is an arm/m1 runner.
@@ -170,6 +181,11 @@ jobs:
cd tests/internal cd tests/internal
make make
timeout-minutes: 10 timeout-minutes: 10
- name: Odin core library benchmarks
run: |
cd tests/benchmark
make
timeout-minutes: 10
build_windows: build_windows:
name: Windows Build, Check, and Test name: Windows Build, Check, and Test
runs-on: windows-2022 runs-on: windows-2022
@@ -217,6 +233,13 @@ jobs:
cd tests\core cd tests\core
call build.bat call build.bat
timeout-minutes: 10 timeout-minutes: 10
- name: Core library benchmarks
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
cd tests\benchmark
call build.bat
timeout-minutes: 10
- name: Vendor library tests - name: Vendor library tests
shell: cmd shell: cmd
run: | run: |
+2 -3
View File
@@ -13,7 +13,7 @@ def main():
for x in files_lines: for x in files_lines:
parts = x.split(" ", 1) parts = x.split(" ", 1)
if parts[0]: if parts[0]:
json_str = execute_cli(f"b2 get-file-info {parts[0]}") json_str = execute_cli(f"b2 file info {parts[0]}")
data = json.loads(json_str) data = json.loads(json_str)
name = remove_prefix(data['fileName'], "nightly/") name = remove_prefix(data['fileName'], "nightly/")
url = f"https://f001.backblazeb2.com/file/{bucket}/nightly/{urllib.parse.quote_plus(name)}" url = f"https://f001.backblazeb2.com/file/{bucket}/nightly/{urllib.parse.quote_plus(name)}"
@@ -47,5 +47,4 @@ def execute_cli(command):
return sb.stdout.read().decode("utf-8"); return sb.stdout.read().decode("utf-8");
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) sys.exit(main())
+2 -4
View File
@@ -12,7 +12,6 @@ def main():
print(f"Looking for binaries to delete older than {days_to_keep} days") print(f"Looking for binaries to delete older than {days_to_keep} days")
files_lines = execute_cli(f"b2 ls --long --versions b2://{bucket}/nightly/").split("\n") files_lines = execute_cli(f"b2 ls --long --versions b2://{bucket}/nightly/").split("\n")
print(files_lines)
for x in files_lines: for x in files_lines:
parts = [y for y in x.split(' ') if y] parts = [y for y in x.split(' ') if y]
@@ -23,7 +22,7 @@ def main():
if delta.days > days_to_keep: if delta.days > days_to_keep:
print(f'Deleting {parts[5]}') print(f'Deleting {parts[5]}')
execute_cli(f'b2 delete-file-version {parts[0]}') execute_cli(f'b2 rm {parts[0]}')
def execute_cli(command): def execute_cli(command):
@@ -31,5 +30,4 @@ def execute_cli(command):
return sb.stdout.read().decode("utf-8"); return sb.stdout.read().decode("utf-8");
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) sys.exit(main())
+1 -1
View File
@@ -22,4 +22,4 @@ else
7z a -bd "output/$filename" -r "$artifact" 7z a -bd "output/$filename" -r "$artifact"
fi fi
b2 upload-file "$bucket" "output/$filename" "nightly/$filename" b2 file upload "$bucket" "output/$filename" "nightly/$filename"
+2 -2
View File
@@ -29,12 +29,12 @@ MIN_READ_BUFFER_SIZE :: 16
@(private) @(private)
DEFAULT_MAX_CONSECUTIVE_EMPTY_READS :: 128 DEFAULT_MAX_CONSECUTIVE_EMPTY_READS :: 128
reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) { reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator, loc := #caller_location) {
size := size size := size
size = max(size, MIN_READ_BUFFER_SIZE) size = max(size, MIN_READ_BUFFER_SIZE)
reader_reset(b, rd) reader_reset(b, rd)
b.buf_allocator = allocator b.buf_allocator = allocator
b.buf = make([]byte, size, allocator) b.buf = make([]byte, size, allocator, loc)
} }
reader_init_with_buf :: proc(b: ^Reader, rd: io.Reader, buf: []byte) { reader_init_with_buf :: proc(b: ^Reader, rd: io.Reader, buf: []byte) {
+35 -35
View File
@@ -27,19 +27,19 @@ Read_Op :: enum i8 {
} }
buffer_init :: proc(b: ^Buffer, buf: []byte) { buffer_init :: proc(b: ^Buffer, buf: []byte, loc := #caller_location) {
resize(&b.buf, len(buf)) resize(&b.buf, len(buf), loc=loc)
copy(b.buf[:], buf) copy(b.buf[:], buf)
} }
buffer_init_string :: proc(b: ^Buffer, s: string) { buffer_init_string :: proc(b: ^Buffer, s: string, loc := #caller_location) {
resize(&b.buf, len(s)) resize(&b.buf, len(s), loc=loc)
copy(b.buf[:], s) copy(b.buf[:], s)
} }
buffer_init_allocator :: proc(b: ^Buffer, len, cap: int, allocator := context.allocator) { buffer_init_allocator :: proc(b: ^Buffer, len, cap: int, allocator := context.allocator, loc := #caller_location) {
if b.buf == nil { if b.buf == nil {
b.buf = make([dynamic]byte, len, cap, allocator) b.buf = make([dynamic]byte, len, cap, allocator, loc)
return return
} }
@@ -96,28 +96,28 @@ buffer_truncate :: proc(b: ^Buffer, n: int) {
} }
@(private) @(private)
_buffer_try_grow :: proc(b: ^Buffer, n: int) -> (int, bool) { _buffer_try_grow :: proc(b: ^Buffer, n: int, loc := #caller_location) -> (int, bool) {
if l := len(b.buf); n <= cap(b.buf)-l { if l := len(b.buf); n <= cap(b.buf)-l {
resize(&b.buf, l+n) resize(&b.buf, l+n, loc=loc)
return l, true return l, true
} }
return 0, false return 0, false
} }
@(private) @(private)
_buffer_grow :: proc(b: ^Buffer, n: int) -> int { _buffer_grow :: proc(b: ^Buffer, n: int, loc := #caller_location) -> int {
m := buffer_length(b) m := buffer_length(b)
if m == 0 && b.off != 0 { if m == 0 && b.off != 0 {
buffer_reset(b) buffer_reset(b)
} }
if i, ok := _buffer_try_grow(b, n); ok { if i, ok := _buffer_try_grow(b, n, loc=loc); ok {
return i return i
} }
if b.buf == nil && n <= SMALL_BUFFER_SIZE { if b.buf == nil && n <= SMALL_BUFFER_SIZE {
// Fixes #2756 by preserving allocator if already set on Buffer via init_buffer_allocator // Fixes #2756 by preserving allocator if already set on Buffer via init_buffer_allocator
reserve(&b.buf, SMALL_BUFFER_SIZE) reserve(&b.buf, SMALL_BUFFER_SIZE, loc=loc)
resize(&b.buf, n) resize(&b.buf, n, loc=loc)
return 0 return 0
} }
@@ -127,31 +127,31 @@ _buffer_grow :: proc(b: ^Buffer, n: int) -> int {
} else if c > max(int) - c - n { } else if c > max(int) - c - n {
panic("bytes.Buffer: too large") panic("bytes.Buffer: too large")
} else { } else {
resize(&b.buf, 2*c + n) resize(&b.buf, 2*c + n, loc=loc)
copy(b.buf[:], b.buf[b.off:]) copy(b.buf[:], b.buf[b.off:])
} }
b.off = 0 b.off = 0
resize(&b.buf, m+n) resize(&b.buf, m+n, loc=loc)
return m return m
} }
buffer_grow :: proc(b: ^Buffer, n: int) { buffer_grow :: proc(b: ^Buffer, n: int, loc := #caller_location) {
if n < 0 { if n < 0 {
panic("bytes.buffer_grow: negative count") panic("bytes.buffer_grow: negative count")
} }
m := _buffer_grow(b, n) m := _buffer_grow(b, n, loc=loc)
resize(&b.buf, m) resize(&b.buf, m, loc=loc)
} }
buffer_write_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) { buffer_write_at :: proc(b: ^Buffer, p: []byte, offset: int, loc := #caller_location) -> (n: int, err: io.Error) {
b.last_read = .Invalid b.last_read = .Invalid
if offset < 0 { if offset < 0 {
err = .Invalid_Offset err = .Invalid_Offset
return return
} }
_, ok := _buffer_try_grow(b, offset+len(p)) _, ok := _buffer_try_grow(b, offset+len(p), loc=loc)
if !ok { if !ok {
_ = _buffer_grow(b, offset+len(p)) _ = _buffer_grow(b, offset+len(p), loc=loc)
} }
if len(b.buf) <= offset { if len(b.buf) <= offset {
return 0, .Short_Write return 0, .Short_Write
@@ -160,47 +160,47 @@ buffer_write_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.
} }
buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) { buffer_write :: proc(b: ^Buffer, p: []byte, loc := #caller_location) -> (n: int, err: io.Error) {
b.last_read = .Invalid b.last_read = .Invalid
m, ok := _buffer_try_grow(b, len(p)) m, ok := _buffer_try_grow(b, len(p), loc=loc)
if !ok { if !ok {
m = _buffer_grow(b, len(p)) m = _buffer_grow(b, len(p), loc=loc)
} }
return copy(b.buf[m:], p), nil return copy(b.buf[m:], p), nil
} }
buffer_write_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.Error) { buffer_write_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int, loc := #caller_location) -> (n: int, err: io.Error) {
return buffer_write(b, ([^]byte)(ptr)[:size]) return buffer_write(b, ([^]byte)(ptr)[:size], loc=loc)
} }
buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) { buffer_write_string :: proc(b: ^Buffer, s: string, loc := #caller_location) -> (n: int, err: io.Error) {
b.last_read = .Invalid b.last_read = .Invalid
m, ok := _buffer_try_grow(b, len(s)) m, ok := _buffer_try_grow(b, len(s), loc=loc)
if !ok { if !ok {
m = _buffer_grow(b, len(s)) m = _buffer_grow(b, len(s), loc=loc)
} }
return copy(b.buf[m:], s), nil return copy(b.buf[m:], s), nil
} }
buffer_write_byte :: proc(b: ^Buffer, c: byte) -> io.Error { buffer_write_byte :: proc(b: ^Buffer, c: byte, loc := #caller_location) -> io.Error {
b.last_read = .Invalid b.last_read = .Invalid
m, ok := _buffer_try_grow(b, 1) m, ok := _buffer_try_grow(b, 1, loc=loc)
if !ok { if !ok {
m = _buffer_grow(b, 1) m = _buffer_grow(b, 1, loc=loc)
} }
b.buf[m] = c b.buf[m] = c
return nil return nil
} }
buffer_write_rune :: proc(b: ^Buffer, r: rune) -> (n: int, err: io.Error) { buffer_write_rune :: proc(b: ^Buffer, r: rune, loc := #caller_location) -> (n: int, err: io.Error) {
if r < utf8.RUNE_SELF { if r < utf8.RUNE_SELF {
buffer_write_byte(b, byte(r)) buffer_write_byte(b, byte(r), loc=loc)
return 1, nil return 1, nil
} }
b.last_read = .Invalid b.last_read = .Invalid
m, ok := _buffer_try_grow(b, utf8.UTF_MAX) m, ok := _buffer_try_grow(b, utf8.UTF_MAX, loc=loc)
if !ok { if !ok {
m = _buffer_grow(b, utf8.UTF_MAX) m = _buffer_grow(b, utf8.UTF_MAX, loc=loc)
} }
res: [4]byte res: [4]byte
res, n = utf8.encode_rune(r) res, n = utf8.encode_rune(r)
+1 -1
View File
@@ -34,7 +34,7 @@ when ODIN_OS == .Windows {
SIGTERM :: 15 SIGTERM :: 15
} }
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD { when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Haiku || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
SIG_ERR :: rawptr(~uintptr(0)) SIG_ERR :: rawptr(~uintptr(0))
SIG_DFL :: rawptr(uintptr(0)) SIG_DFL :: rawptr(uintptr(0))
SIG_IGN :: rawptr(uintptr(1)) SIG_IGN :: rawptr(uintptr(1))
+8 -6
View File
@@ -102,10 +102,12 @@ when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
SEEK_END :: 2 SEEK_END :: 2
foreign libc { foreign libc {
stderr: ^FILE __sF: [3]FILE
stdin: ^FILE
stdout: ^FILE
} }
stdin: ^FILE = &__sF[0]
stdout: ^FILE = &__sF[1]
stderr: ^FILE = &__sF[2]
} }
when ODIN_OS == .FreeBSD { when ODIN_OS == .FreeBSD {
@@ -127,9 +129,9 @@ when ODIN_OS == .FreeBSD {
SEEK_END :: 2 SEEK_END :: 2
foreign libc { foreign libc {
stderr: ^FILE @(link_name="__stderrp") stderr: ^FILE
stdin: ^FILE @(link_name="__stdinp") stdin: ^FILE
stdout: ^FILE @(link_name="__stdoutp") stdout: ^FILE
} }
} }
+137
View File
@@ -0,0 +1,137 @@
package ansi
BEL :: "\a" // Bell
BS :: "\b" // Backspace
ESC :: "\e" // Escape
// Fe Escape sequences
CSI :: ESC + "[" // Control Sequence Introducer
OSC :: ESC + "]" // Operating System Command
ST :: ESC + "\\" // String Terminator
// CSI sequences
CUU :: "A" // Cursor Up
CUD :: "B" // Cursor Down
CUF :: "C" // Cursor Forward
CUB :: "D" // Cursor Back
CNL :: "E" // Cursor Next Line
CPL :: "F" // Cursor Previous Line
CHA :: "G" // Cursor Horizontal Absolute
CUP :: "H" // Cursor Position
ED :: "J" // Erase in Display
EL :: "K" // Erase in Line
SU :: "S" // Scroll Up
SD :: "T" // Scroll Down
HVP :: "f" // Horizontal Vertical Position
SGR :: "m" // Select Graphic Rendition
AUX_ON :: "5i" // AUX Port On
AUX_OFF :: "4i" // AUX Port Off
DSR :: "6n" // Device Status Report
// CSI: private sequences
SCP :: "s" // Save Current Cursor Position
RCP :: "u" // Restore Saved Cursor Position
DECAWM_ON :: "?7h" // Auto Wrap Mode (Enabled)
DECAWM_OFF :: "?7l" // Auto Wrap Mode (Disabled)
DECTCEM_SHOW :: "?25h" // Text Cursor Enable Mode (Visible)
DECTCEM_HIDE :: "?25l" // Text Cursor Enable Mode (Invisible)
// SGR sequences
RESET :: "0"
BOLD :: "1"
FAINT :: "2"
ITALIC :: "3" // Not widely supported.
UNDERLINE :: "4"
BLINK_SLOW :: "5"
BLINK_RAPID :: "6" // Not widely supported.
INVERT :: "7" // Also known as reverse video.
HIDE :: "8" // Not widely supported.
STRIKE :: "9"
FONT_PRIMARY :: "10"
FONT_ALT1 :: "11"
FONT_ALT2 :: "12"
FONT_ALT3 :: "13"
FONT_ALT4 :: "14"
FONT_ALT5 :: "15"
FONT_ALT6 :: "16"
FONT_ALT7 :: "17"
FONT_ALT8 :: "18"
FONT_ALT9 :: "19"
FONT_FRAKTUR :: "20" // Rarely supported.
UNDERLINE_DOUBLE :: "21" // May be interpreted as "disable bold."
NO_BOLD_FAINT :: "22"
NO_ITALIC_BLACKLETTER :: "23"
NO_UNDERLINE :: "24"
NO_BLINK :: "25"
PROPORTIONAL_SPACING :: "26"
NO_REVERSE :: "27"
NO_HIDE :: "28"
NO_STRIKE :: "29"
FG_BLACK :: "30"
FG_RED :: "31"
FG_GREEN :: "32"
FG_YELLOW :: "33"
FG_BLUE :: "34"
FG_MAGENTA :: "35"
FG_CYAN :: "36"
FG_WHITE :: "37"
FG_COLOR :: "38"
FG_COLOR_8_BIT :: "38;5" // Followed by ";n" where n is in 0..=255
FG_COLOR_24_BIT :: "38;2" // Followed by ";r;g;b" where r,g,b are in 0..=255
FG_DEFAULT :: "39"
BG_BLACK :: "40"
BG_RED :: "41"
BG_GREEN :: "42"
BG_YELLOW :: "43"
BG_BLUE :: "44"
BG_MAGENTA :: "45"
BG_CYAN :: "46"
BG_WHITE :: "47"
BG_COLOR :: "48"
BG_COLOR_8_BIT :: "48;5" // Followed by ";n" where n is in 0..=255
BG_COLOR_24_BIT :: "48;2" // Followed by ";r;g;b" where r,g,b are in 0..=255
BG_DEFAULT :: "49"
NO_PROPORTIONAL_SPACING :: "50"
FRAMED :: "51"
ENCIRCLED :: "52"
OVERLINED :: "53"
NO_FRAME_ENCIRCLE :: "54"
NO_OVERLINE :: "55"
// SGR: non-standard bright colors
FG_BRIGHT_BLACK :: "90" // Also known as grey.
FG_BRIGHT_RED :: "91"
FG_BRIGHT_GREEN :: "92"
FG_BRIGHT_YELLOW :: "93"
FG_BRIGHT_BLUE :: "94"
FG_BRIGHT_MAGENTA :: "95"
FG_BRIGHT_CYAN :: "96"
FG_BRIGHT_WHITE :: "97"
BG_BRIGHT_BLACK :: "100" // Also known as grey.
BG_BRIGHT_RED :: "101"
BG_BRIGHT_GREEN :: "102"
BG_BRIGHT_YELLOW :: "103"
BG_BRIGHT_BLUE :: "104"
BG_BRIGHT_MAGENTA :: "105"
BG_BRIGHT_CYAN :: "106"
BG_BRIGHT_WHITE :: "107"
// Fp Escape sequences
DECSC :: ESC + "7" // DEC Save Cursor
DECRC :: ESC + "8" // DEC Restore Cursor
// OSC sequences
WINDOW_TITLE :: "2" // Followed by ";<text>" ST.
HYPERLINK :: "8" // Followed by ";[params];<URI>" ST. Closed by OSC HYPERLINK ";;" ST.
CLIPBOARD :: "52" // Followed by ";c;<Base64-encoded string>" ST.
+20
View File
@@ -0,0 +1,20 @@
/*
package ansi implements constant references to many widely-supported ANSI
escape codes, primarily used in terminal emulators for enhanced graphics, such
as colors, text styling, and animated displays.
For example, you can print out a line of cyan text like this:
fmt.println(ansi.CSI + ansi.FG_CYAN + ansi.SGR + "Hellope!" + ansi.CSI + ansi.RESET + ansi.SGR)
Multiple SGR (Select Graphic Rendition) codes can be joined by semicolons:
fmt.println(ansi.CSI + ansi.BOLD + ";" + ansi.FG_BLUE + ansi.SGR + "Hellope!" + ansi.CSI + ansi.RESET + ansi.SGR)
If your terminal supports 24-bit true color mode, you can also do this:
fmt.println(ansi.CSI + ansi.FG_COLOR_24_BIT + ";0;255;255" + ansi.SGR + "Hellope!" + ansi.CSI + ansi.RESET + ansi.SGR)
For more information, see:
1. https://en.wikipedia.org/wiki/ANSI_escape_code
2. https://www.vt100.net/docs/vt102-ug/chapter5.html
3. https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
*/
package ansi
+2 -2
View File
@@ -320,8 +320,8 @@ to_diagnostic_format :: proc {
// Turns the given CBOR value into a human-readable string. // Turns the given CBOR value into a human-readable string.
// See docs on the proc group `diagnose` for more info. // See docs on the proc group `diagnose` for more info.
to_diagnostic_format_string :: proc(val: Value, padding := 0, allocator := context.allocator) -> (string, mem.Allocator_Error) #optional_allocator_error { to_diagnostic_format_string :: proc(val: Value, padding := 0, allocator := context.allocator, loc := #caller_location) -> (string, mem.Allocator_Error) #optional_allocator_error {
b := strings.builder_make(allocator) b := strings.builder_make(allocator, loc)
w := strings.to_stream(&b) w := strings.to_stream(&b)
err := to_diagnostic_format_writer(w, val, padding) err := to_diagnostic_format_writer(w, val, padding)
if err == .EOF { if err == .EOF {
+54 -53
View File
@@ -95,24 +95,25 @@ decode :: decode_from
// Decodes the given string as CBOR. // Decodes the given string as CBOR.
// See docs on the proc group `decode` for more information. // See docs on the proc group `decode` for more information.
decode_from_string :: proc(s: string, flags: Decoder_Flags = {}, allocator := context.allocator) -> (v: Value, err: Decode_Error) { decode_from_string :: proc(s: string, flags: Decoder_Flags = {}, allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) {
r: strings.Reader r: strings.Reader
strings.reader_init(&r, s) strings.reader_init(&r, s)
return decode_from_reader(strings.reader_to_stream(&r), flags, allocator) return decode_from_reader(strings.reader_to_stream(&r), flags, allocator, loc)
} }
// Reads a CBOR value from the given reader. // Reads a CBOR value from the given reader.
// See docs on the proc group `decode` for more information. // See docs on the proc group `decode` for more information.
decode_from_reader :: proc(r: io.Reader, flags: Decoder_Flags = {}, allocator := context.allocator) -> (v: Value, err: Decode_Error) { decode_from_reader :: proc(r: io.Reader, flags: Decoder_Flags = {}, allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) {
return decode_from_decoder( return decode_from_decoder(
Decoder{ DEFAULT_MAX_PRE_ALLOC, flags, r }, Decoder{ DEFAULT_MAX_PRE_ALLOC, flags, r },
allocator=allocator, allocator=allocator,
loc = loc,
) )
} }
// Reads a CBOR value from the given decoder. // Reads a CBOR value from the given decoder.
// See docs on the proc group `decode` for more information. // See docs on the proc group `decode` for more information.
decode_from_decoder :: proc(d: Decoder, allocator := context.allocator) -> (v: Value, err: Decode_Error) { decode_from_decoder :: proc(d: Decoder, allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) {
context.allocator = allocator context.allocator = allocator
d := d d := d
@@ -121,13 +122,13 @@ decode_from_decoder :: proc(d: Decoder, allocator := context.allocator) -> (v: V
d.max_pre_alloc = DEFAULT_MAX_PRE_ALLOC d.max_pre_alloc = DEFAULT_MAX_PRE_ALLOC
} }
v, err = _decode_from_decoder(d) v, err = _decode_from_decoder(d, {}, allocator, loc)
// Normal EOF does not exist here, we try to read the exact amount that is said to be provided. // Normal EOF does not exist here, we try to read the exact amount that is said to be provided.
if err == .EOF { err = .Unexpected_EOF } if err == .EOF { err = .Unexpected_EOF }
return return
} }
_decode_from_decoder :: proc(d: Decoder, hdr: Header = Header(0)) -> (v: Value, err: Decode_Error) { _decode_from_decoder :: proc(d: Decoder, hdr: Header = Header(0), allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) {
hdr := hdr hdr := hdr
r := d.reader r := d.reader
if hdr == Header(0) { hdr = _decode_header(r) or_return } if hdr == Header(0) { hdr = _decode_header(r) or_return }
@@ -161,11 +162,11 @@ _decode_from_decoder :: proc(d: Decoder, hdr: Header = Header(0)) -> (v: Value,
switch maj { switch maj {
case .Unsigned: return _decode_tiny_u8(add) case .Unsigned: return _decode_tiny_u8(add)
case .Negative: return Negative_U8(_decode_tiny_u8(add) or_return), nil case .Negative: return Negative_U8(_decode_tiny_u8(add) or_return), nil
case .Bytes: return _decode_bytes_ptr(d, add) case .Bytes: return _decode_bytes_ptr(d, add, .Bytes, allocator, loc)
case .Text: return _decode_text_ptr(d, add) case .Text: return _decode_text_ptr(d, add, allocator, loc)
case .Array: return _decode_array_ptr(d, add) case .Array: return _decode_array_ptr(d, add, allocator, loc)
case .Map: return _decode_map_ptr(d, add) case .Map: return _decode_map_ptr(d, add, allocator, loc)
case .Tag: return _decode_tag_ptr(d, add) case .Tag: return _decode_tag_ptr(d, add, allocator, loc)
case .Other: return _decode_tiny_simple(add) case .Other: return _decode_tiny_simple(add)
case: return nil, .Bad_Major case: return nil, .Bad_Major
} }
@@ -203,27 +204,27 @@ encode :: encode_into
// Encodes the CBOR value into binary CBOR allocated on the given allocator. // Encodes the CBOR value into binary CBOR allocated on the given allocator.
// See the docs on the proc group `encode_into` for more info. // See the docs on the proc group `encode_into` for more info.
encode_into_bytes :: proc(v: Value, flags := ENCODE_SMALL, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (data: []byte, err: Encode_Error) { encode_into_bytes :: proc(v: Value, flags := ENCODE_SMALL, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (data: []byte, err: Encode_Error) {
b := strings.builder_make(allocator) or_return b := strings.builder_make(allocator, loc) or_return
encode_into_builder(&b, v, flags, temp_allocator) or_return encode_into_builder(&b, v, flags, temp_allocator) or_return
return b.buf[:], nil return b.buf[:], nil
} }
// Encodes the CBOR value into binary CBOR written to the given builder. // Encodes the CBOR value into binary CBOR written to the given builder.
// See the docs on the proc group `encode_into` for more info. // See the docs on the proc group `encode_into` for more info.
encode_into_builder :: proc(b: ^strings.Builder, v: Value, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Encode_Error { encode_into_builder :: proc(b: ^strings.Builder, v: Value, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator, loc := #caller_location) -> Encode_Error {
return encode_into_writer(strings.to_stream(b), v, flags, temp_allocator) return encode_into_writer(strings.to_stream(b), v, flags, temp_allocator, loc=loc)
} }
// Encodes the CBOR value into binary CBOR written to the given writer. // Encodes the CBOR value into binary CBOR written to the given writer.
// See the docs on the proc group `encode_into` for more info. // See the docs on the proc group `encode_into` for more info.
encode_into_writer :: proc(w: io.Writer, v: Value, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Encode_Error { encode_into_writer :: proc(w: io.Writer, v: Value, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator, loc := #caller_location) -> Encode_Error {
return encode_into_encoder(Encoder{flags, w, temp_allocator}, v) return encode_into_encoder(Encoder{flags, w, temp_allocator}, v, loc=loc)
} }
// Encodes the CBOR value into binary CBOR written to the given encoder. // Encodes the CBOR value into binary CBOR written to the given encoder.
// See the docs on the proc group `encode_into` for more info. // See the docs on the proc group `encode_into` for more info.
encode_into_encoder :: proc(e: Encoder, v: Value) -> Encode_Error { encode_into_encoder :: proc(e: Encoder, v: Value, loc := #caller_location) -> Encode_Error {
e := e e := e
if e.temp_allocator.procedure == nil { if e.temp_allocator.procedure == nil {
@@ -366,21 +367,21 @@ _encode_u64_exact :: proc(w: io.Writer, v: u64, major: Major = .Unsigned) -> (er
return return
} }
_decode_bytes_ptr :: proc(d: Decoder, add: Add, type: Major = .Bytes) -> (v: ^Bytes, err: Decode_Error) { _decode_bytes_ptr :: proc(d: Decoder, add: Add, type: Major = .Bytes, allocator := context.allocator, loc := #caller_location) -> (v: ^Bytes, err: Decode_Error) {
v = new(Bytes) or_return v = new(Bytes, allocator, loc) or_return
defer if err != nil { free(v) } defer if err != nil { free(v, allocator, loc) }
v^ = _decode_bytes(d, add, type) or_return v^ = _decode_bytes(d, add, type, allocator, loc) or_return
return return
} }
_decode_bytes :: proc(d: Decoder, add: Add, type: Major = .Bytes, allocator := context.allocator) -> (v: Bytes, err: Decode_Error) { _decode_bytes :: proc(d: Decoder, add: Add, type: Major = .Bytes, allocator := context.allocator, loc := #caller_location) -> (v: Bytes, err: Decode_Error) {
context.allocator = allocator context.allocator = allocator
add := add add := add
n, scap := _decode_len_str(d, add) or_return n, scap := _decode_len_str(d, add) or_return
buf := strings.builder_make(0, scap) or_return buf := strings.builder_make(0, scap, allocator, loc) or_return
defer if err != nil { strings.builder_destroy(&buf) } defer if err != nil { strings.builder_destroy(&buf) }
buf_stream := strings.to_stream(&buf) buf_stream := strings.to_stream(&buf)
@@ -426,40 +427,40 @@ _encode_bytes :: proc(e: Encoder, val: Bytes, major: Major = .Bytes) -> (err: En
return return
} }
_decode_text_ptr :: proc(d: Decoder, add: Add) -> (v: ^Text, err: Decode_Error) { _decode_text_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: ^Text, err: Decode_Error) {
v = new(Text) or_return v = new(Text, allocator, loc) or_return
defer if err != nil { free(v) } defer if err != nil { free(v) }
v^ = _decode_text(d, add) or_return v^ = _decode_text(d, add, allocator, loc) or_return
return return
} }
_decode_text :: proc(d: Decoder, add: Add, allocator := context.allocator) -> (v: Text, err: Decode_Error) { _decode_text :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Text, err: Decode_Error) {
return (Text)(_decode_bytes(d, add, .Text, allocator) or_return), nil return (Text)(_decode_bytes(d, add, .Text, allocator, loc) or_return), nil
} }
_encode_text :: proc(e: Encoder, val: Text) -> Encode_Error { _encode_text :: proc(e: Encoder, val: Text) -> Encode_Error {
return _encode_bytes(e, transmute([]byte)val, .Text) return _encode_bytes(e, transmute([]byte)val, .Text)
} }
_decode_array_ptr :: proc(d: Decoder, add: Add) -> (v: ^Array, err: Decode_Error) { _decode_array_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: ^Array, err: Decode_Error) {
v = new(Array) or_return v = new(Array, allocator, loc) or_return
defer if err != nil { free(v) } defer if err != nil { free(v) }
v^ = _decode_array(d, add) or_return v^ = _decode_array(d, add, allocator, loc) or_return
return return
} }
_decode_array :: proc(d: Decoder, add: Add) -> (v: Array, err: Decode_Error) { _decode_array :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Array, err: Decode_Error) {
n, scap := _decode_len_container(d, add) or_return n, scap := _decode_len_container(d, add) or_return
array := make([dynamic]Value, 0, scap) or_return array := make([dynamic]Value, 0, scap, allocator, loc) or_return
defer if err != nil { defer if err != nil {
for entry in array { destroy(entry) } for entry in array { destroy(entry, allocator) }
delete(array) delete(array, loc)
} }
for i := 0; n == -1 || i < n; i += 1 { for i := 0; n == -1 || i < n; i += 1 {
val, verr := _decode_from_decoder(d) val, verr := _decode_from_decoder(d, {}, allocator, loc)
if n == -1 && verr == .Break { if n == -1 && verr == .Break {
break break
} else if verr != nil { } else if verr != nil {
@@ -485,39 +486,39 @@ _encode_array :: proc(e: Encoder, arr: Array) -> Encode_Error {
return nil return nil
} }
_decode_map_ptr :: proc(d: Decoder, add: Add) -> (v: ^Map, err: Decode_Error) { _decode_map_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: ^Map, err: Decode_Error) {
v = new(Map) or_return v = new(Map, allocator, loc) or_return
defer if err != nil { free(v) } defer if err != nil { free(v) }
v^ = _decode_map(d, add) or_return v^ = _decode_map(d, add, allocator, loc) or_return
return return
} }
_decode_map :: proc(d: Decoder, add: Add) -> (v: Map, err: Decode_Error) { _decode_map :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Map, err: Decode_Error) {
n, scap := _decode_len_container(d, add) or_return n, scap := _decode_len_container(d, add) or_return
items := make([dynamic]Map_Entry, 0, scap) or_return items := make([dynamic]Map_Entry, 0, scap, allocator, loc) or_return
defer if err != nil { defer if err != nil {
for entry in items { for entry in items {
destroy(entry.key) destroy(entry.key)
destroy(entry.value) destroy(entry.value)
} }
delete(items) delete(items, loc)
} }
for i := 0; n == -1 || i < n; i += 1 { for i := 0; n == -1 || i < n; i += 1 {
key, kerr := _decode_from_decoder(d) key, kerr := _decode_from_decoder(d, {}, allocator, loc)
if n == -1 && kerr == .Break { if n == -1 && kerr == .Break {
break break
} else if kerr != nil { } else if kerr != nil {
return nil, kerr return nil, kerr
} }
value := _decode_from_decoder(d) or_return value := _decode_from_decoder(d, {}, allocator, loc) or_return
append(&items, Map_Entry{ append(&items, Map_Entry{
key = key, key = key,
value = value, value = value,
}) or_return }, loc) or_return
} }
if .Shrink_Excess in d.flags { shrink(&items) } if .Shrink_Excess in d.flags { shrink(&items) }
@@ -578,20 +579,20 @@ _encode_map :: proc(e: Encoder, m: Map) -> (err: Encode_Error) {
return nil return nil
} }
_decode_tag_ptr :: proc(d: Decoder, add: Add) -> (v: Value, err: Decode_Error) { _decode_tag_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) {
tag := _decode_tag(d, add) or_return tag := _decode_tag(d, add, allocator, loc) or_return
if t, ok := tag.?; ok { if t, ok := tag.?; ok {
defer if err != nil { destroy(t.value) } defer if err != nil { destroy(t.value) }
tp := new(Tag) or_return tp := new(Tag, allocator, loc) or_return
tp^ = t tp^ = t
return tp, nil return tp, nil
} }
// no error, no tag, this was the self described CBOR tag, skip it. // no error, no tag, this was the self described CBOR tag, skip it.
return _decode_from_decoder(d) return _decode_from_decoder(d, {}, allocator, loc)
} }
_decode_tag :: proc(d: Decoder, add: Add) -> (v: Maybe(Tag), err: Decode_Error) { _decode_tag :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Maybe(Tag), err: Decode_Error) {
num := _decode_uint_as_u64(d.reader, add) or_return num := _decode_uint_as_u64(d.reader, add) or_return
// CBOR can be wrapped in a tag that decoders can use to see/check if the binary data is CBOR. // CBOR can be wrapped in a tag that decoders can use to see/check if the binary data is CBOR.
@@ -602,7 +603,7 @@ _decode_tag :: proc(d: Decoder, add: Add) -> (v: Maybe(Tag), err: Decode_Error)
t := Tag{ t := Tag{
number = num, number = num,
value = _decode_from_decoder(d) or_return, value = _decode_from_decoder(d, {}, allocator, loc) or_return,
} }
if nested, ok := t.value.(^Tag); ok { if nested, ok := t.value.(^Tag); ok {
@@ -883,4 +884,4 @@ _encode_deterministic_f64 :: proc(w: io.Writer, v: f64) -> io.Error {
} }
return _encode_f64_exact(w, v) return _encode_f64_exact(w, v)
} }
+8 -8
View File
@@ -45,8 +45,8 @@ marshal :: marshal_into
// Marshals the given value into a CBOR byte stream (allocated using the given allocator). // Marshals the given value into a CBOR byte stream (allocated using the given allocator).
// See docs on the `marshal_into` proc group for more info. // See docs on the `marshal_into` proc group for more info.
marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (bytes: []byte, err: Marshal_Error) { marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (bytes: []byte, err: Marshal_Error) {
b, alloc_err := strings.builder_make(allocator) b, alloc_err := strings.builder_make(allocator, loc=loc)
// The builder as a stream also returns .EOF if it ran out of memory so this is consistent. // The builder as a stream also returns .EOF if it ran out of memory so this is consistent.
if alloc_err != nil { if alloc_err != nil {
return nil, .EOF return nil, .EOF
@@ -54,7 +54,7 @@ marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.a
defer if err != nil { strings.builder_destroy(&b) } defer if err != nil { strings.builder_destroy(&b) }
if err = marshal_into_builder(&b, v, flags, temp_allocator); err != nil { if err = marshal_into_builder(&b, v, flags, temp_allocator, loc=loc); err != nil {
return return
} }
@@ -63,20 +63,20 @@ marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.a
// Marshals the given value into a CBOR byte stream written to the given builder. // Marshals the given value into a CBOR byte stream written to the given builder.
// See docs on the `marshal_into` proc group for more info. // See docs on the `marshal_into` proc group for more info.
marshal_into_builder :: proc(b: ^strings.Builder, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Marshal_Error { marshal_into_builder :: proc(b: ^strings.Builder, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator, loc := #caller_location) -> Marshal_Error {
return marshal_into_writer(strings.to_writer(b), v, flags, temp_allocator) return marshal_into_writer(strings.to_writer(b), v, flags, temp_allocator, loc=loc)
} }
// Marshals the given value into a CBOR byte stream written to the given writer. // Marshals the given value into a CBOR byte stream written to the given writer.
// See docs on the `marshal_into` proc group for more info. // See docs on the `marshal_into` proc group for more info.
marshal_into_writer :: proc(w: io.Writer, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Marshal_Error { marshal_into_writer :: proc(w: io.Writer, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator, loc := #caller_location) -> Marshal_Error {
encoder := Encoder{flags, w, temp_allocator} encoder := Encoder{flags, w, temp_allocator}
return marshal_into_encoder(encoder, v) return marshal_into_encoder(encoder, v, loc=loc)
} }
// Marshals the given value into a CBOR byte stream written to the given encoder. // Marshals the given value into a CBOR byte stream written to the given encoder.
// See docs on the `marshal_into` proc group for more info. // See docs on the `marshal_into` proc group for more info.
marshal_into_encoder :: proc(e: Encoder, v: any) -> (err: Marshal_Error) { marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (err: Marshal_Error) {
e := e e := e
if e.temp_allocator.procedure == nil { if e.temp_allocator.procedure == nil {
+52 -50
View File
@@ -31,8 +31,8 @@ unmarshal :: proc {
unmarshal_from_string, unmarshal_from_string,
} }
unmarshal_from_reader :: proc(r: io.Reader, ptr: ^$T, flags := Decoder_Flags{}, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (err: Unmarshal_Error) { unmarshal_from_reader :: proc(r: io.Reader, ptr: ^$T, flags := Decoder_Flags{}, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
err = unmarshal_from_decoder(Decoder{ DEFAULT_MAX_PRE_ALLOC, flags, r }, ptr, allocator, temp_allocator) err = unmarshal_from_decoder(Decoder{ DEFAULT_MAX_PRE_ALLOC, flags, r }, ptr, allocator, temp_allocator, loc)
// Normal EOF does not exist here, we try to read the exact amount that is said to be provided. // Normal EOF does not exist here, we try to read the exact amount that is said to be provided.
if err == .EOF { err = .Unexpected_EOF } if err == .EOF { err = .Unexpected_EOF }
@@ -40,21 +40,21 @@ unmarshal_from_reader :: proc(r: io.Reader, ptr: ^$T, flags := Decoder_Flags{},
} }
// Unmarshals from a string, see docs on the proc group `Unmarshal` for more info. // Unmarshals from a string, see docs on the proc group `Unmarshal` for more info.
unmarshal_from_string :: proc(s: string, ptr: ^$T, flags := Decoder_Flags{}, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (err: Unmarshal_Error) { unmarshal_from_string :: proc(s: string, ptr: ^$T, flags := Decoder_Flags{}, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
sr: strings.Reader sr: strings.Reader
r := strings.to_reader(&sr, s) r := strings.to_reader(&sr, s)
err = unmarshal_from_reader(r, ptr, flags, allocator, temp_allocator) err = unmarshal_from_reader(r, ptr, flags, allocator, temp_allocator, loc)
// Normal EOF does not exist here, we try to read the exact amount that is said to be provided. // Normal EOF does not exist here, we try to read the exact amount that is said to be provided.
if err == .EOF { err = .Unexpected_EOF } if err == .EOF { err = .Unexpected_EOF }
return return
} }
unmarshal_from_decoder :: proc(d: Decoder, ptr: ^$T, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (err: Unmarshal_Error) { unmarshal_from_decoder :: proc(d: Decoder, ptr: ^$T, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
d := d d := d
err = _unmarshal_any_ptr(d, ptr, nil, allocator, temp_allocator) err = _unmarshal_any_ptr(d, ptr, nil, allocator, temp_allocator, loc)
// Normal EOF does not exist here, we try to read the exact amount that is said to be provided. // Normal EOF does not exist here, we try to read the exact amount that is said to be provided.
if err == .EOF { err = .Unexpected_EOF } if err == .EOF { err = .Unexpected_EOF }
@@ -62,7 +62,7 @@ unmarshal_from_decoder :: proc(d: Decoder, ptr: ^$T, allocator := context.alloca
} }
_unmarshal_any_ptr :: proc(d: Decoder, v: any, hdr: Maybe(Header) = nil, allocator := context.allocator, temp_allocator := context.temp_allocator) -> Unmarshal_Error { _unmarshal_any_ptr :: proc(d: Decoder, v: any, hdr: Maybe(Header) = nil, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> Unmarshal_Error {
context.allocator = allocator context.allocator = allocator
context.temp_allocator = temp_allocator context.temp_allocator = temp_allocator
v := v v := v
@@ -78,10 +78,10 @@ _unmarshal_any_ptr :: proc(d: Decoder, v: any, hdr: Maybe(Header) = nil, allocat
} }
data := any{(^rawptr)(v.data)^, ti.variant.(reflect.Type_Info_Pointer).elem.id} data := any{(^rawptr)(v.data)^, ti.variant.(reflect.Type_Info_Pointer).elem.id}
return _unmarshal_value(d, data, hdr.? or_else (_decode_header(d.reader) or_return)) return _unmarshal_value(d, data, hdr.? or_else (_decode_header(d.reader) or_return), allocator, temp_allocator, loc)
} }
_unmarshal_value :: proc(d: Decoder, v: any, hdr: Header) -> (err: Unmarshal_Error) { _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
v := v v := v
ti := reflect.type_info_base(type_info_of(v.id)) ti := reflect.type_info_base(type_info_of(v.id))
r := d.reader r := d.reader
@@ -104,7 +104,7 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header) -> (err: Unmarshal_Err
// Allow generic unmarshal by doing it into a `Value`. // Allow generic unmarshal by doing it into a `Value`.
switch &dst in v { switch &dst in v {
case Value: case Value:
dst = err_conv(_decode_from_decoder(d, hdr)) or_return dst = err_conv(_decode_from_decoder(d, hdr, allocator, loc)) or_return
return return
} }
@@ -308,7 +308,7 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header) -> (err: Unmarshal_Err
if impl, ok := _tag_implementations_nr[nr]; ok { if impl, ok := _tag_implementations_nr[nr]; ok {
return impl->unmarshal(d, nr, v) return impl->unmarshal(d, nr, v)
} else if nr == TAG_OBJECT_TYPE { } else if nr == TAG_OBJECT_TYPE {
return _unmarshal_union(d, v, ti, hdr) return _unmarshal_union(d, v, ti, hdr, loc=loc)
} else { } else {
// Discard the tag info and unmarshal as its value. // Discard the tag info and unmarshal as its value.
return _unmarshal_value(d, v, _decode_header(r) or_return) return _unmarshal_value(d, v, _decode_header(r) or_return)
@@ -316,19 +316,19 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header) -> (err: Unmarshal_Err
return _unsupported(v, hdr, add) return _unsupported(v, hdr, add)
case .Bytes: return _unmarshal_bytes(d, v, ti, hdr, add) case .Bytes: return _unmarshal_bytes(d, v, ti, hdr, add, allocator=allocator, loc=loc)
case .Text: return _unmarshal_string(d, v, ti, hdr, add) case .Text: return _unmarshal_string(d, v, ti, hdr, add, allocator=allocator, loc=loc)
case .Array: return _unmarshal_array(d, v, ti, hdr, add) case .Array: return _unmarshal_array(d, v, ti, hdr, add, allocator=allocator, loc=loc)
case .Map: return _unmarshal_map(d, v, ti, hdr, add) case .Map: return _unmarshal_map(d, v, ti, hdr, add, allocator=allocator, loc=loc)
case: return .Bad_Major case: return .Bad_Major
} }
} }
_unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add) -> (err: Unmarshal_Error) { _unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add, allocator := context.allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
#partial switch t in ti.variant { #partial switch t in ti.variant {
case reflect.Type_Info_String: case reflect.Type_Info_String:
bytes := err_conv(_decode_bytes(d, add)) or_return bytes := err_conv(_decode_bytes(d, add, allocator=allocator, loc=loc)) or_return
if t.is_cstring { if t.is_cstring {
raw := (^cstring)(v.data) raw := (^cstring)(v.data)
@@ -347,7 +347,7 @@ _unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
if elem_base.id != byte { return _unsupported(v, hdr) } if elem_base.id != byte { return _unsupported(v, hdr) }
bytes := err_conv(_decode_bytes(d, add)) or_return bytes := err_conv(_decode_bytes(d, add, allocator=allocator, loc=loc)) or_return
raw := (^mem.Raw_Slice)(v.data) raw := (^mem.Raw_Slice)(v.data)
raw^ = transmute(mem.Raw_Slice)bytes raw^ = transmute(mem.Raw_Slice)bytes
return return
@@ -357,12 +357,12 @@ _unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
if elem_base.id != byte { return _unsupported(v, hdr) } if elem_base.id != byte { return _unsupported(v, hdr) }
bytes := err_conv(_decode_bytes(d, add)) or_return bytes := err_conv(_decode_bytes(d, add, allocator=allocator, loc=loc)) or_return
raw := (^mem.Raw_Dynamic_Array)(v.data) raw := (^mem.Raw_Dynamic_Array)(v.data)
raw.data = raw_data(bytes) raw.data = raw_data(bytes)
raw.len = len(bytes) raw.len = len(bytes)
raw.cap = len(bytes) raw.cap = len(bytes)
raw.allocator = context.allocator raw.allocator = allocator
return return
case reflect.Type_Info_Array: case reflect.Type_Info_Array:
@@ -385,10 +385,10 @@ _unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
return _unsupported(v, hdr) return _unsupported(v, hdr)
} }
_unmarshal_string :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add) -> (err: Unmarshal_Error) { _unmarshal_string :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
#partial switch t in ti.variant { #partial switch t in ti.variant {
case reflect.Type_Info_String: case reflect.Type_Info_String:
text := err_conv(_decode_text(d, add)) or_return text := err_conv(_decode_text(d, add, allocator, loc)) or_return
if t.is_cstring { if t.is_cstring {
raw := (^cstring)(v.data) raw := (^cstring)(v.data)
@@ -403,8 +403,8 @@ _unmarshal_string :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Heade
// Enum by its variant name. // Enum by its variant name.
case reflect.Type_Info_Enum: case reflect.Type_Info_Enum:
text := err_conv(_decode_text(d, add, allocator=context.temp_allocator)) or_return text := err_conv(_decode_text(d, add, allocator=temp_allocator, loc=loc)) or_return
defer delete(text, context.temp_allocator) defer delete(text, temp_allocator, loc)
for name, i in t.names { for name, i in t.names {
if name == text { if name == text {
@@ -414,8 +414,8 @@ _unmarshal_string :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Heade
} }
case reflect.Type_Info_Rune: case reflect.Type_Info_Rune:
text := err_conv(_decode_text(d, add, allocator=context.temp_allocator)) or_return text := err_conv(_decode_text(d, add, allocator=temp_allocator, loc=loc)) or_return
defer delete(text, context.temp_allocator) defer delete(text, temp_allocator, loc)
r := (^rune)(v.data) r := (^rune)(v.data)
dr, n := utf8.decode_rune(text) dr, n := utf8.decode_rune(text)
@@ -430,13 +430,15 @@ _unmarshal_string :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Heade
return _unsupported(v, hdr) return _unsupported(v, hdr)
} }
_unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add) -> (err: Unmarshal_Error) { _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add, allocator := context.allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
assign_array :: proc( assign_array :: proc(
d: Decoder, d: Decoder,
da: ^mem.Raw_Dynamic_Array, da: ^mem.Raw_Dynamic_Array,
elemt: ^reflect.Type_Info, elemt: ^reflect.Type_Info,
length: int, length: int,
growable := true, growable := true,
allocator := context.allocator,
loc := #caller_location,
) -> (out_of_space: bool, err: Unmarshal_Error) { ) -> (out_of_space: bool, err: Unmarshal_Error) {
for idx: uintptr = 0; length == -1 || idx < uintptr(length); idx += 1 { for idx: uintptr = 0; length == -1 || idx < uintptr(length); idx += 1 {
elem_ptr := rawptr(uintptr(da.data) + idx*uintptr(elemt.size)) elem_ptr := rawptr(uintptr(da.data) + idx*uintptr(elemt.size))
@@ -450,13 +452,13 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
if !growable { return true, .Out_Of_Memory } if !growable { return true, .Out_Of_Memory }
cap := 2 * da.cap cap := 2 * da.cap
ok := runtime.__dynamic_array_reserve(da, elemt.size, elemt.align, cap) ok := runtime.__dynamic_array_reserve(da, elemt.size, elemt.align, cap, loc)
// NOTE: Might be lying here, but it is at least an allocator error. // NOTE: Might be lying here, but it is at least an allocator error.
if !ok { return false, .Out_Of_Memory } if !ok { return false, .Out_Of_Memory }
} }
err = _unmarshal_value(d, elem, hdr) err = _unmarshal_value(d, elem, hdr, allocator=allocator, loc=loc)
if length == -1 && err == .Break { break } if length == -1 && err == .Break { break }
if err != nil { return } if err != nil { return }
@@ -469,10 +471,10 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
// Allow generically storing the values array. // Allow generically storing the values array.
switch &dst in v { switch &dst in v {
case ^Array: case ^Array:
dst = err_conv(_decode_array_ptr(d, add)) or_return dst = err_conv(_decode_array_ptr(d, add, allocator=allocator, loc=loc)) or_return
return return
case Array: case Array:
dst = err_conv(_decode_array(d, add)) or_return dst = err_conv(_decode_array(d, add, allocator=allocator, loc=loc)) or_return
return return
} }
@@ -480,8 +482,8 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
case reflect.Type_Info_Slice: case reflect.Type_Info_Slice:
length, scap := err_conv(_decode_len_container(d, add)) or_return length, scap := err_conv(_decode_len_container(d, add)) or_return
data := mem.alloc_bytes_non_zeroed(t.elem.size * scap, t.elem.align) or_return data := mem.alloc_bytes_non_zeroed(t.elem.size * scap, t.elem.align, allocator=allocator, loc=loc) or_return
defer if err != nil { mem.free_bytes(data) } defer if err != nil { mem.free_bytes(data, allocator=allocator, loc=loc) }
da := mem.Raw_Dynamic_Array{raw_data(data), 0, length, context.allocator } da := mem.Raw_Dynamic_Array{raw_data(data), 0, length, context.allocator }
@@ -489,7 +491,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
if .Shrink_Excess in d.flags { if .Shrink_Excess in d.flags {
// Ignoring an error here, but this is not critical to succeed. // Ignoring an error here, but this is not critical to succeed.
_ = runtime.__dynamic_array_shrink(&da, t.elem.size, t.elem.align, da.len) _ = runtime.__dynamic_array_shrink(&da, t.elem.size, t.elem.align, da.len, loc=loc)
} }
raw := (^mem.Raw_Slice)(v.data) raw := (^mem.Raw_Slice)(v.data)
@@ -500,8 +502,8 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
case reflect.Type_Info_Dynamic_Array: case reflect.Type_Info_Dynamic_Array:
length, scap := err_conv(_decode_len_container(d, add)) or_return length, scap := err_conv(_decode_len_container(d, add)) or_return
data := mem.alloc_bytes_non_zeroed(t.elem.size * scap, t.elem.align) or_return data := mem.alloc_bytes_non_zeroed(t.elem.size * scap, t.elem.align, loc=loc) or_return
defer if err != nil { mem.free_bytes(data) } defer if err != nil { mem.free_bytes(data, allocator=allocator, loc=loc) }
raw := (^mem.Raw_Dynamic_Array)(v.data) raw := (^mem.Raw_Dynamic_Array)(v.data)
raw.data = raw_data(data) raw.data = raw_data(data)
@@ -513,7 +515,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
if .Shrink_Excess in d.flags { if .Shrink_Excess in d.flags {
// Ignoring an error here, but this is not critical to succeed. // Ignoring an error here, but this is not critical to succeed.
_ = runtime.__dynamic_array_shrink(raw, t.elem.size, t.elem.align, raw.len) _ = runtime.__dynamic_array_shrink(raw, t.elem.size, t.elem.align, raw.len, loc=loc)
} }
return return
@@ -525,7 +527,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
return _unsupported(v, hdr) return _unsupported(v, hdr)
} }
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, context.allocator } da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, allocator }
out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return
if out_of_space { return _unsupported(v, hdr) } if out_of_space { return _unsupported(v, hdr) }
@@ -539,7 +541,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
return _unsupported(v, hdr) return _unsupported(v, hdr)
} }
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, context.allocator } da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, allocator }
out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return
if out_of_space { return _unsupported(v, hdr) } if out_of_space { return _unsupported(v, hdr) }
@@ -553,7 +555,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
return _unsupported(v, hdr) return _unsupported(v, hdr)
} }
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, 2, context.allocator } da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, 2, allocator }
info: ^runtime.Type_Info info: ^runtime.Type_Info
switch ti.id { switch ti.id {
@@ -575,7 +577,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
return _unsupported(v, hdr) return _unsupported(v, hdr)
} }
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, 4, context.allocator } da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, 4, allocator }
info: ^runtime.Type_Info info: ^runtime.Type_Info
switch ti.id { switch ti.id {
@@ -593,17 +595,17 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
} }
} }
_unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add) -> (err: Unmarshal_Error) { _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add, allocator := context.allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
r := d.reader r := d.reader
decode_key :: proc(d: Decoder, v: any, allocator := context.allocator) -> (k: string, err: Unmarshal_Error) { decode_key :: proc(d: Decoder, v: any, allocator := context.allocator, loc := #caller_location) -> (k: string, err: Unmarshal_Error) {
entry_hdr := _decode_header(d.reader) or_return entry_hdr := _decode_header(d.reader) or_return
entry_maj, entry_add := _header_split(entry_hdr) entry_maj, entry_add := _header_split(entry_hdr)
#partial switch entry_maj { #partial switch entry_maj {
case .Text: case .Text:
k = err_conv(_decode_text(d, entry_add, allocator)) or_return k = err_conv(_decode_text(d, entry_add, allocator=allocator, loc=loc)) or_return
return return
case .Bytes: case .Bytes:
bytes := err_conv(_decode_bytes(d, entry_add, allocator=allocator)) or_return bytes := err_conv(_decode_bytes(d, entry_add, allocator=allocator, loc=loc)) or_return
k = string(bytes) k = string(bytes)
return return
case: case:
@@ -615,10 +617,10 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
// Allow generically storing the map array. // Allow generically storing the map array.
switch &dst in v { switch &dst in v {
case ^Map: case ^Map:
dst = err_conv(_decode_map_ptr(d, add)) or_return dst = err_conv(_decode_map_ptr(d, add, allocator=allocator, loc=loc)) or_return
return return
case Map: case Map:
dst = err_conv(_decode_map(d, add)) or_return dst = err_conv(_decode_map(d, add, allocator=allocator, loc=loc)) or_return
return return
} }
@@ -754,7 +756,7 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
// Unmarshal into a union, based on the `TAG_OBJECT_TYPE` tag of the spec, it denotes a tag which // Unmarshal into a union, based on the `TAG_OBJECT_TYPE` tag of the spec, it denotes a tag which
// contains an array of exactly two elements, the first is a textual representation of the following // contains an array of exactly two elements, the first is a textual representation of the following
// CBOR value's type. // CBOR value's type.
_unmarshal_union :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header) -> (err: Unmarshal_Error) { _unmarshal_union :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, loc := #caller_location) -> (err: Unmarshal_Error) {
r := d.reader r := d.reader
#partial switch t in ti.variant { #partial switch t in ti.variant {
case reflect.Type_Info_Union: case reflect.Type_Info_Union:
@@ -792,7 +794,7 @@ _unmarshal_union :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
case reflect.Type_Info_Named: case reflect.Type_Info_Named:
if vti.name == target_name { if vti.name == target_name {
reflect.set_union_variant_raw_tag(v, tag) reflect.set_union_variant_raw_tag(v, tag)
return _unmarshal_value(d, any{v.data, variant.id}, _decode_header(r) or_return) return _unmarshal_value(d, any{v.data, variant.id}, _decode_header(r) or_return, loc=loc)
} }
case: case:
@@ -804,7 +806,7 @@ _unmarshal_union :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
if variant_name == target_name { if variant_name == target_name {
reflect.set_union_variant_raw_tag(v, tag) reflect.set_union_variant_raw_tag(v, tag)
return _unmarshal_value(d, any{v.data, variant.id}, _decode_header(r) or_return) return _unmarshal_value(d, any{v.data, variant.id}, _decode_header(r) or_return, loc=loc)
} }
} }
} }
+5 -6
View File
@@ -2,8 +2,8 @@ package encoding_hex
import "core:strings" import "core:strings"
encode :: proc(src: []byte, allocator := context.allocator) -> []byte #no_bounds_check { encode :: proc(src: []byte, allocator := context.allocator, loc := #caller_location) -> []byte #no_bounds_check {
dst := make([]byte, len(src) * 2, allocator) dst := make([]byte, len(src) * 2, allocator, loc)
for i, j := 0, 0; i < len(src); i += 1 { for i, j := 0, 0; i < len(src); i += 1 {
v := src[i] v := src[i]
dst[j] = HEXTABLE[v>>4] dst[j] = HEXTABLE[v>>4]
@@ -15,12 +15,12 @@ encode :: proc(src: []byte, allocator := context.allocator) -> []byte #no_bounds
} }
decode :: proc(src: []byte, allocator := context.allocator) -> (dst: []byte, ok: bool) #no_bounds_check { decode :: proc(src: []byte, allocator := context.allocator, loc := #caller_location) -> (dst: []byte, ok: bool) #no_bounds_check {
if len(src) % 2 == 1 { if len(src) % 2 == 1 {
return return
} }
dst = make([]byte, len(src) / 2, allocator) dst = make([]byte, len(src) / 2, allocator, loc)
for i, j := 0, 1; j < len(src); j += 2 { for i, j := 0, 1; j < len(src); j += 2 {
p := src[j-1] p := src[j-1]
q := src[j] q := src[j]
@@ -69,5 +69,4 @@ hex_digit :: proc(char: byte) -> (u8, bool) {
case 'A' ..= 'F': return char - 'A' + 10, true case 'A' ..= 'F': return char - 'A' + 10, true
case: return 0, false case: return 0, false
} }
} }
+16 -15
View File
@@ -160,34 +160,35 @@ CONVENTION_SOFT_TRANSFORM :: "transform"
/* destroy procedures */ /* destroy procedures */
meta_destroy :: proc(meta: Meta, allocator := context.allocator) { meta_destroy :: proc(meta: Meta, allocator := context.allocator, loc := #caller_location) {
if nested, ok := meta.value.([]Meta); ok { if nested, ok := meta.value.([]Meta); ok {
for m in nested { for m in nested {
meta_destroy(m) meta_destroy(m, loc=loc)
} }
delete(nested, allocator) delete(nested, allocator, loc=loc)
} }
} }
nodes_destroy :: proc(nodes: []Node, allocator := context.allocator) { nodes_destroy :: proc(nodes: []Node, allocator := context.allocator, loc := #caller_location) {
for node in nodes { for node in nodes {
for meta in node.meta_data { for meta in node.meta_data {
meta_destroy(meta) meta_destroy(meta, loc=loc)
} }
delete(node.meta_data, allocator) delete(node.meta_data, allocator, loc=loc)
switch n in node.content { switch n in node.content {
case Node_Geometry: case Node_Geometry:
delete(n.corner_stack, allocator) delete(n.corner_stack, allocator, loc=loc)
delete(n.edge_stack, allocator) delete(n.vertex_stack, allocator, loc=loc)
delete(n.face_stack, allocator) delete(n.edge_stack, allocator, loc=loc)
delete(n.face_stack, allocator, loc=loc)
case Node_Image: case Node_Image:
delete(n.image_stack, allocator) delete(n.image_stack, allocator, loc=loc)
} }
} }
delete(nodes, allocator) delete(nodes, allocator, loc=loc)
} }
file_destroy :: proc(file: File) { file_destroy :: proc(file: File, loc := #caller_location) {
nodes_destroy(file.nodes, file.allocator) nodes_destroy(file.nodes, file.allocator, loc=loc)
delete(file.backing, file.allocator) delete(file.backing, file.allocator, loc=loc)
} }
+20 -22
View File
@@ -11,24 +11,21 @@ Read_Error :: enum {
Unable_To_Read_File, Unable_To_Read_File,
} }
read_from_file :: proc(filename: string, print_error := false, allocator := context.allocator) -> (file: File, err: Read_Error) { read_from_file :: proc(filename: string, print_error := false, allocator := context.allocator, loc := #caller_location) -> (file: File, err: Read_Error) {
context.allocator = allocator context.allocator = allocator
data, ok := os.read_entire_file(filename) data, ok := os.read_entire_file(filename, allocator, loc)
if !ok { if !ok {
err = .Unable_To_Read_File err = .Unable_To_Read_File
delete(data, allocator, loc)
return return
} }
defer if !ok { file, err = read(data, filename, print_error, allocator, loc)
delete(data) file.backing = data
} else {
file.backing = data
}
file, err = read(data, filename, print_error, allocator)
return return
} }
read :: proc(data: []byte, filename := "<input>", print_error := false, allocator := context.allocator) -> (file: File, err: Read_Error) { read :: proc(data: []byte, filename := "<input>", print_error := false, allocator := context.allocator, loc := #caller_location) -> (file: File, err: Read_Error) {
Reader :: struct { Reader :: struct {
filename: string, filename: string,
data: []byte, data: []byte,
@@ -79,8 +76,8 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
return string(data[:len]), nil return string(data[:len]), nil
} }
read_meta :: proc(r: ^Reader, capacity: u32le) -> (meta_data: []Meta, err: Read_Error) { read_meta :: proc(r: ^Reader, capacity: u32le, allocator := context.allocator, loc := #caller_location) -> (meta_data: []Meta, err: Read_Error) {
meta_data = make([]Meta, int(capacity)) meta_data = make([]Meta, int(capacity), allocator=allocator)
count := 0 count := 0
defer meta_data = meta_data[:count] defer meta_data = meta_data[:count]
for &m in meta_data { for &m in meta_data {
@@ -111,10 +108,10 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
return return
} }
read_layer_stack :: proc(r: ^Reader, capacity: u32le) -> (layers: Layer_Stack, err: Read_Error) { read_layer_stack :: proc(r: ^Reader, capacity: u32le, allocator := context.allocator, loc := #caller_location) -> (layers: Layer_Stack, err: Read_Error) {
stack_count := read_value(r, u32le) or_return stack_count := read_value(r, u32le) or_return
layer_count := 0 layer_count := 0
layers = make(Layer_Stack, stack_count) layers = make(Layer_Stack, stack_count, allocator=allocator, loc=loc)
defer layers = layers[:layer_count] defer layers = layers[:layer_count]
for &layer in layers { for &layer in layers {
layer.name = read_name(r) or_return layer.name = read_name(r) or_return
@@ -170,7 +167,8 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
node_count := 0 node_count := 0
file.header = header^ file.header = header^
file.nodes = make([]Node, header.internal_node_count) file.nodes = make([]Node, header.internal_node_count, allocator=allocator, loc=loc)
file.allocator = allocator
defer if err != nil { defer if err != nil {
nodes_destroy(file.nodes) nodes_destroy(file.nodes)
file.nodes = nil file.nodes = nil
@@ -198,15 +196,15 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
case .Geometry: case .Geometry:
g: Node_Geometry g: Node_Geometry
g.vertex_count = read_value(r, u32le) or_return g.vertex_count = read_value(r, u32le) or_return
g.vertex_stack = read_layer_stack(r, g.vertex_count) or_return g.vertex_stack = read_layer_stack(r, g.vertex_count, loc=loc) or_return
g.edge_corner_count = read_value(r, u32le) or_return g.edge_corner_count = read_value(r, u32le) or_return
g.corner_stack = read_layer_stack(r, g.edge_corner_count) or_return g.corner_stack = read_layer_stack(r, g.edge_corner_count, loc=loc) or_return
if header.version > 2 { if header.version > 2 {
g.edge_stack = read_layer_stack(r, g.edge_corner_count) or_return g.edge_stack = read_layer_stack(r, g.edge_corner_count, loc=loc) or_return
} }
g.face_count = read_value(r, u32le) or_return g.face_count = read_value(r, u32le) or_return
g.face_stack = read_layer_stack(r, g.face_count) or_return g.face_stack = read_layer_stack(r, g.face_count, loc=loc) or_return
node.content = g node.content = g
@@ -233,4 +231,4 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
} }
return return
} }
+2 -2
View File
@@ -62,8 +62,8 @@ Marshal_Options :: struct {
mjson_skipped_first_braces_end: bool, mjson_skipped_first_braces_end: bool,
} }
marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) { marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Marshal_Error) {
b := strings.builder_make(allocator) b := strings.builder_make(allocator, loc)
defer if err != nil { defer if err != nil {
strings.builder_destroy(&b) strings.builder_destroy(&b)
} }
+33 -32
View File
@@ -28,27 +28,27 @@ make_parser_from_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, par
} }
parse :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> (Value, Error) { parse :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) {
return parse_string(string(data), spec, parse_integers, allocator) return parse_string(string(data), spec, parse_integers, allocator, loc)
} }
parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> (Value, Error) { parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) {
context.allocator = allocator context.allocator = allocator
p := make_parser_from_string(data, spec, parse_integers, allocator) p := make_parser_from_string(data, spec, parse_integers, allocator)
switch p.spec { switch p.spec {
case .JSON: case .JSON:
return parse_object(&p) return parse_object(&p, loc)
case .JSON5: case .JSON5:
return parse_value(&p) return parse_value(&p, loc)
case .SJSON: case .SJSON:
#partial switch p.curr_token.kind { #partial switch p.curr_token.kind {
case .Ident, .String: case .Ident, .String:
return parse_object_body(&p, .EOF) return parse_object_body(&p, .EOF, loc)
} }
return parse_value(&p) return parse_value(&p, loc)
} }
return parse_object(&p) return parse_object(&p, loc)
} }
token_end_pos :: proc(tok: Token) -> Pos { token_end_pos :: proc(tok: Token) -> Pos {
@@ -106,7 +106,7 @@ parse_comma :: proc(p: ^Parser) -> (do_break: bool) {
return false return false
} }
parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) { parse_value :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) {
err = .None err = .None
token := p.curr_token token := p.curr_token
#partial switch token.kind { #partial switch token.kind {
@@ -142,13 +142,13 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
case .String: case .String:
advance_token(p) advance_token(p)
return unquote_string(token, p.spec, p.allocator) return unquote_string(token, p.spec, p.allocator, loc)
case .Open_Brace: case .Open_Brace:
return parse_object(p) return parse_object(p, loc)
case .Open_Bracket: case .Open_Bracket:
return parse_array(p) return parse_array(p, loc)
case: case:
if p.spec != .JSON { if p.spec != .JSON {
@@ -176,7 +176,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
return return
} }
parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) { parse_array :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) {
err = .None err = .None
expect_token(p, .Open_Bracket) or_return expect_token(p, .Open_Bracket) or_return
@@ -184,14 +184,14 @@ parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
array.allocator = p.allocator array.allocator = p.allocator
defer if err != nil { defer if err != nil {
for elem in array { for elem in array {
destroy_value(elem) destroy_value(elem, loc=loc)
} }
delete(array) delete(array, loc)
} }
for p.curr_token.kind != .Close_Bracket { for p.curr_token.kind != .Close_Bracket {
elem := parse_value(p) or_return elem := parse_value(p, loc) or_return
append(&array, elem) append(&array, elem, loc)
if parse_comma(p) { if parse_comma(p) {
break break
@@ -228,38 +228,39 @@ clone_string :: proc(s: string, allocator: mem.Allocator, loc := #caller_locatio
return return
} }
parse_object_key :: proc(p: ^Parser, key_allocator: mem.Allocator) -> (key: string, err: Error) { parse_object_key :: proc(p: ^Parser, key_allocator: mem.Allocator, loc := #caller_location) -> (key: string, err: Error) {
tok := p.curr_token tok := p.curr_token
if p.spec != .JSON { if p.spec != .JSON {
if allow_token(p, .Ident) { if allow_token(p, .Ident) {
return clone_string(tok.text, key_allocator) return clone_string(tok.text, key_allocator, loc)
} }
} }
if tok_err := expect_token(p, .String); tok_err != nil { if tok_err := expect_token(p, .String); tok_err != nil {
err = .Expected_String_For_Object_Key err = .Expected_String_For_Object_Key
return return
} }
return unquote_string(tok, p.spec, key_allocator) return unquote_string(tok, p.spec, key_allocator, loc)
} }
parse_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> (obj: Object, err: Error) { parse_object_body :: proc(p: ^Parser, end_token: Token_Kind, loc := #caller_location) -> (obj: Object, err: Error) {
obj.allocator = p.allocator obj = make(Object, allocator=p.allocator, loc=loc)
defer if err != nil { defer if err != nil {
for key, elem in obj { for key, elem in obj {
delete(key, p.allocator) delete(key, p.allocator, loc)
destroy_value(elem) destroy_value(elem, loc=loc)
} }
delete(obj) delete(obj, loc)
} }
for p.curr_token.kind != end_token { for p.curr_token.kind != end_token {
key := parse_object_key(p, p.allocator) or_return key := parse_object_key(p, p.allocator, loc) or_return
parse_colon(p) or_return parse_colon(p) or_return
elem := parse_value(p) or_return elem := parse_value(p, loc) or_return
if key in obj { if key in obj {
err = .Duplicate_Object_Key err = .Duplicate_Object_Key
delete(key, p.allocator) delete(key, p.allocator, loc)
return return
} }
@@ -267,7 +268,7 @@ parse_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> (obj: Object, er
// inserting empty key/values into the object and for those we do not // inserting empty key/values into the object and for those we do not
// want to allocate anything // want to allocate anything
if key != "" { if key != "" {
reserve_error := reserve(&obj, len(obj) + 1) reserve_error := reserve(&obj, len(obj) + 1, loc)
if reserve_error == mem.Allocator_Error.Out_Of_Memory { if reserve_error == mem.Allocator_Error.Out_Of_Memory {
return nil, .Out_Of_Memory return nil, .Out_Of_Memory
} }
@@ -281,9 +282,9 @@ parse_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> (obj: Object, er
return obj, .None return obj, .None
} }
parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) { parse_object :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) {
expect_token(p, .Open_Brace) or_return expect_token(p, .Open_Brace) or_return
obj := parse_object_body(p, .Close_Brace) or_return obj := parse_object_body(p, .Close_Brace, loc) or_return
expect_token(p, .Close_Brace) or_return expect_token(p, .Close_Brace) or_return
return obj, .None return obj, .None
} }
@@ -480,4 +481,4 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
} }
return string(b[:w]), nil return string(b[:w]), nil
} }
+7 -7
View File
@@ -89,22 +89,22 @@ Error :: enum {
destroy_value :: proc(value: Value, allocator := context.allocator) { destroy_value :: proc(value: Value, allocator := context.allocator, loc := #caller_location) {
context.allocator = allocator context.allocator = allocator
#partial switch v in value { #partial switch v in value {
case Object: case Object:
for key, elem in v { for key, elem in v {
delete(key) delete(key, loc=loc)
destroy_value(elem) destroy_value(elem, loc=loc)
} }
delete(v) delete(v, loc=loc)
case Array: case Array:
for elem in v { for elem in v {
destroy_value(elem) destroy_value(elem, loc=loc)
} }
delete(v) delete(v, loc=loc)
case String: case String:
delete(v) delete(v, loc=loc)
} }
} }
+26 -15
View File
@@ -1,6 +1,7 @@
//+build !freestanding //+build !freestanding
package log package log
import "core:encoding/ansi"
import "core:fmt" import "core:fmt"
import "core:strings" import "core:strings"
import "core:os" import "core:os"
@@ -70,18 +71,10 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string
backing: [1024]byte //NOTE(Hoej): 1024 might be too much for a header backing, unless somebody has really long paths. backing: [1024]byte //NOTE(Hoej): 1024 might be too much for a header backing, unless somebody has really long paths.
buf := strings.builder_from_bytes(backing[:]) buf := strings.builder_from_bytes(backing[:])
do_level_header(options, level, &buf) do_level_header(options, &buf, level)
when time.IS_SUPPORTED { when time.IS_SUPPORTED {
if Full_Timestamp_Opts & options != nil { do_time_header(options, &buf, time.now())
fmt.sbprint(&buf, "[")
t := time.now()
y, m, d := time.date(t)
h, min, s := time.clock(t)
if .Date in options { fmt.sbprintf(&buf, "%d-%02d-%02d ", y, m, d) }
if .Time in options { fmt.sbprintf(&buf, "%02d:%02d:%02d", h, min, s) }
fmt.sbprint(&buf, "] ")
}
} }
do_location_header(options, &buf, location) do_location_header(options, &buf, location)
@@ -99,12 +92,12 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string
fmt.fprintf(h, "%s%s\n", strings.to_string(buf), text) fmt.fprintf(h, "%s%s\n", strings.to_string(buf), text)
} }
do_level_header :: proc(opts: Options, level: Level, str: ^strings.Builder) { do_level_header :: proc(opts: Options, str: ^strings.Builder, level: Level) {
RESET :: "\x1b[0m" RESET :: ansi.CSI + ansi.RESET + ansi.SGR
RED :: "\x1b[31m" RED :: ansi.CSI + ansi.FG_RED + ansi.SGR
YELLOW :: "\x1b[33m" YELLOW :: ansi.CSI + ansi.FG_YELLOW + ansi.SGR
DARK_GREY :: "\x1b[90m" DARK_GREY :: ansi.CSI + ansi.FG_BRIGHT_BLACK + ansi.SGR
col := RESET col := RESET
switch level { switch level {
@@ -125,6 +118,24 @@ do_level_header :: proc(opts: Options, level: Level, str: ^strings.Builder) {
} }
} }
do_time_header :: proc(opts: Options, buf: ^strings.Builder, t: time.Time) {
when time.IS_SUPPORTED {
if Full_Timestamp_Opts & opts != nil {
fmt.sbprint(buf, "[")
y, m, d := time.date(t)
h, min, s := time.clock(t)
if .Date in opts {
fmt.sbprintf(buf, "%d-%02d-%02d", y, m, d)
if .Time in opts {
fmt.sbprint(buf, " ")
}
}
if .Time in opts { fmt.sbprintf(buf, "%02d:%02d:%02d", h, min, s) }
fmt.sbprint(buf, "] ")
}
}
}
do_location_header :: proc(opts: Options, buf: ^strings.Builder, location := #caller_location) { do_location_header :: proc(opts: Options, buf: ^strings.Builder, location := #caller_location) {
if Location_Header_Opts & opts == nil { if Location_Header_Opts & opts == nil {
return return
+341
View File
@@ -0,0 +1,341 @@
package mem
// The Rollback Stack Allocator was designed for the test runner to be fast,
// able to grow, and respect the Tracking Allocator's requirement for
// individual frees. It is not overly concerned with fragmentation, however.
//
// It has support for expansion when configured with a block allocator and
// limited support for out-of-order frees.
//
// Allocation has constant-time best and usual case performance.
// At worst, it is linear according to the number of memory blocks.
//
// Allocation follows a first-fit strategy when there are multiple memory
// blocks.
//
// Freeing has constant-time best and usual case performance.
// At worst, it is linear according to the number of memory blocks and number
// of freed items preceding the last item in a block.
//
// Resizing has constant-time performance, if it's the last item in a block, or
// the new size is smaller. Naturally, this becomes linear-time if there are
// multiple blocks to search for the pointer's owning block. Otherwise, the
// allocator defaults to a combined alloc & free operation internally.
//
// Out-of-order freeing is accomplished by collapsing a run of freed items
// from the last allocation backwards.
//
// Each allocation has an overhead of 8 bytes and any extra bytes to satisfy
// the requested alignment.
import "base:runtime"
ROLLBACK_STACK_DEFAULT_BLOCK_SIZE :: 4 * Megabyte
// This limitation is due to the size of `prev_ptr`, but it is only for the
// head block; any allocation in excess of the allocator's `block_size` is
// valid, so long as the block allocator can handle it.
//
// This is because allocations over the block size are not split up if the item
// within is freed; they are immediately returned to the block allocator.
ROLLBACK_STACK_MAX_HEAD_BLOCK_SIZE :: 2 * Gigabyte
Rollback_Stack_Header :: bit_field u64 {
prev_offset: uintptr | 32,
is_free: bool | 1,
prev_ptr: uintptr | 31,
}
Rollback_Stack_Block :: struct {
next_block: ^Rollback_Stack_Block,
last_alloc: rawptr,
offset: uintptr,
buffer: []byte,
}
Rollback_Stack :: struct {
head: ^Rollback_Stack_Block,
block_size: int,
block_allocator: Allocator,
}
@(private="file", require_results)
rb_ptr_in_bounds :: proc(block: ^Rollback_Stack_Block, ptr: rawptr) -> bool {
start := raw_data(block.buffer)
end := start[block.offset:]
return start < ptr && ptr <= end
}
@(private="file", require_results)
rb_find_ptr :: proc(stack: ^Rollback_Stack, ptr: rawptr) -> (
parent: ^Rollback_Stack_Block,
block: ^Rollback_Stack_Block,
header: ^Rollback_Stack_Header,
err: Allocator_Error,
) {
for block = stack.head; block != nil; block = block.next_block {
if rb_ptr_in_bounds(block, ptr) {
header = cast(^Rollback_Stack_Header)(cast(uintptr)ptr - size_of(Rollback_Stack_Header))
return
}
parent = block
}
return nil, nil, nil, .Invalid_Pointer
}
@(private="file", require_results)
rb_find_last_alloc :: proc(stack: ^Rollback_Stack, ptr: rawptr) -> (
block: ^Rollback_Stack_Block,
header: ^Rollback_Stack_Header,
ok: bool,
) {
for block = stack.head; block != nil; block = block.next_block {
if block.last_alloc == ptr {
header = cast(^Rollback_Stack_Header)(cast(uintptr)ptr - size_of(Rollback_Stack_Header))
return block, header, true
}
}
return nil, nil, false
}
@(private="file")
rb_rollback_block :: proc(block: ^Rollback_Stack_Block, header: ^Rollback_Stack_Header) {
header := header
for block.offset > 0 && header.is_free {
block.offset = header.prev_offset
block.last_alloc = raw_data(block.buffer)[header.prev_ptr:]
header = cast(^Rollback_Stack_Header)(raw_data(block.buffer)[header.prev_ptr - size_of(Rollback_Stack_Header):])
}
}
@(private="file", require_results)
rb_free :: proc(stack: ^Rollback_Stack, ptr: rawptr) -> Allocator_Error {
parent, block, header := rb_find_ptr(stack, ptr) or_return
if header.is_free {
return .Invalid_Pointer
}
header.is_free = true
if block.last_alloc == ptr {
block.offset = header.prev_offset
rb_rollback_block(block, header)
}
if parent != nil && block.offset == 0 {
parent.next_block = block.next_block
runtime.mem_free_with_size(block, size_of(Rollback_Stack_Block) + len(block.buffer), stack.block_allocator)
}
return nil
}
@(private="file")
rb_free_all :: proc(stack: ^Rollback_Stack) {
for block := stack.head.next_block; block != nil; /**/ {
next_block := block.next_block
runtime.mem_free_with_size(block, size_of(Rollback_Stack_Block) + len(block.buffer), stack.block_allocator)
block = next_block
}
stack.head.next_block = nil
stack.head.last_alloc = nil
stack.head.offset = 0
}
@(private="file", require_results)
rb_resize :: proc(stack: ^Rollback_Stack, ptr: rawptr, old_size, size, alignment: int) -> (result: []byte, err: Allocator_Error) {
if ptr != nil {
if block, _, ok := rb_find_last_alloc(stack, ptr); ok {
// `block.offset` should never underflow because it is contingent
// on `old_size` in the first place, assuming sane arguments.
assert(block.offset >= cast(uintptr)old_size, "Rollback Stack Allocator received invalid `old_size`.")
if block.offset + cast(uintptr)size - cast(uintptr)old_size < cast(uintptr)len(block.buffer) {
// Prevent singleton allocations from fragmenting by forbidding
// them to shrink, removing the possibility of overflow bugs.
if len(block.buffer) <= stack.block_size {
block.offset += cast(uintptr)size - cast(uintptr)old_size
}
#no_bounds_check return (cast([^]byte)ptr)[:size], nil
}
}
}
result = rb_alloc(stack, size, alignment) or_return
runtime.mem_copy_non_overlapping(raw_data(result), ptr, old_size)
err = rb_free(stack, ptr)
return
}
@(private="file", require_results)
rb_alloc :: proc(stack: ^Rollback_Stack, size, alignment: int) -> (result: []byte, err: Allocator_Error) {
parent: ^Rollback_Stack_Block
for block := stack.head; /**/; block = block.next_block {
when !ODIN_DISABLE_ASSERT {
allocated_new_block: bool
}
if block == nil {
if stack.block_allocator.procedure == nil {
return nil, .Out_Of_Memory
}
minimum_size_required := size_of(Rollback_Stack_Header) + size + alignment - 1
new_block_size := max(minimum_size_required, stack.block_size)
block = rb_make_block(new_block_size, stack.block_allocator) or_return
parent.next_block = block
when !ODIN_DISABLE_ASSERT {
allocated_new_block = true
}
}
start := raw_data(block.buffer)[block.offset:]
padding := cast(uintptr)calc_padding_with_header(cast(uintptr)start, cast(uintptr)alignment, size_of(Rollback_Stack_Header))
if block.offset + padding + cast(uintptr)size > cast(uintptr)len(block.buffer) {
when !ODIN_DISABLE_ASSERT {
if allocated_new_block {
panic("Rollback Stack Allocator allocated a new block but did not use it.")
}
}
parent = block
continue
}
header := cast(^Rollback_Stack_Header)(start[padding - size_of(Rollback_Stack_Header):])
ptr := start[padding:]
header^ = {
prev_offset = block.offset,
prev_ptr = uintptr(0) if block.last_alloc == nil else cast(uintptr)block.last_alloc - cast(uintptr)raw_data(block.buffer),
is_free = false,
}
block.last_alloc = ptr
block.offset += padding + cast(uintptr)size
if len(block.buffer) > stack.block_size {
// This block exceeds the allocator's standard block size and is considered a singleton.
// Prevent any further allocations on it.
block.offset = cast(uintptr)len(block.buffer)
}
#no_bounds_check return ptr[:size], nil
}
return nil, .Out_Of_Memory
}
@(private="file", require_results)
rb_make_block :: proc(size: int, allocator: Allocator) -> (block: ^Rollback_Stack_Block, err: Allocator_Error) {
buffer := runtime.mem_alloc(size_of(Rollback_Stack_Block) + size, align_of(Rollback_Stack_Block), allocator) or_return
block = cast(^Rollback_Stack_Block)raw_data(buffer)
#no_bounds_check block.buffer = buffer[size_of(Rollback_Stack_Block):]
return
}
rollback_stack_init_buffered :: proc(stack: ^Rollback_Stack, buffer: []byte, location := #caller_location) {
MIN_SIZE :: size_of(Rollback_Stack_Block) + size_of(Rollback_Stack_Header) + size_of(rawptr)
assert(len(buffer) >= MIN_SIZE, "User-provided buffer to Rollback Stack Allocator is too small.", location)
block := cast(^Rollback_Stack_Block)raw_data(buffer)
block^ = {}
#no_bounds_check block.buffer = buffer[size_of(Rollback_Stack_Block):]
stack^ = {}
stack.head = block
stack.block_size = len(block.buffer)
}
rollback_stack_init_dynamic :: proc(
stack: ^Rollback_Stack,
block_size : int = ROLLBACK_STACK_DEFAULT_BLOCK_SIZE,
block_allocator := context.allocator,
location := #caller_location,
) -> Allocator_Error {
assert(block_size >= size_of(Rollback_Stack_Header) + size_of(rawptr), "Rollback Stack Allocator block size is too small.", location)
when size_of(int) > 4 {
// It's impossible to specify an argument in excess when your integer
// size is insufficient; check only on platforms with big enough ints.
assert(block_size <= ROLLBACK_STACK_MAX_HEAD_BLOCK_SIZE, "Rollback Stack Allocators cannot support head blocks larger than 2 gigabytes.", location)
}
block := rb_make_block(block_size, block_allocator) or_return
stack^ = {}
stack.head = block
stack.block_size = block_size
stack.block_allocator = block_allocator
return nil
}
rollback_stack_init :: proc {
rollback_stack_init_buffered,
rollback_stack_init_dynamic,
}
rollback_stack_destroy :: proc(stack: ^Rollback_Stack) {
if stack.block_allocator.procedure != nil {
rb_free_all(stack)
free(stack.head, stack.block_allocator)
}
stack^ = {}
}
@(require_results)
rollback_stack_allocator :: proc(stack: ^Rollback_Stack) -> Allocator {
return Allocator {
data = stack,
procedure = rollback_stack_allocator_proc,
}
}
@(require_results)
rollback_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, location := #caller_location,
) -> (result: []byte, err: Allocator_Error) {
stack := cast(^Rollback_Stack)allocator_data
switch mode {
case .Alloc, .Alloc_Non_Zeroed:
assert(size >= 0, "Size must be positive or zero.", location)
assert(is_power_of_two(cast(uintptr)alignment), "Alignment must be a power of two.", location)
result = rb_alloc(stack, size, alignment) or_return
if mode == .Alloc {
zero_slice(result)
}
case .Free:
err = rb_free(stack, old_memory)
case .Free_All:
rb_free_all(stack)
case .Resize, .Resize_Non_Zeroed:
assert(size >= 0, "Size must be positive or zero.", location)
assert(old_size >= 0, "Old size must be positive or zero.", location)
assert(is_power_of_two(cast(uintptr)alignment), "Alignment must be a power of two.", location)
result = rb_resize(stack, old_memory, old_size, size, alignment) or_return
#no_bounds_check if mode == .Resize && size > old_size {
zero_slice(result[old_size:])
}
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory)
if set != nil {
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Resize_Non_Zeroed}
}
return nil, nil
case .Query_Info:
return nil, .Mode_Not_Implemented
}
return
}
+14
View File
@@ -47,6 +47,7 @@ tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
} }
// Clear only the current allocation data while keeping the totals intact.
tracking_allocator_clear :: proc(t: ^Tracking_Allocator) { tracking_allocator_clear :: proc(t: ^Tracking_Allocator) {
sync.mutex_lock(&t.mutex) sync.mutex_lock(&t.mutex)
clear(&t.allocation_map) clear(&t.allocation_map)
@@ -55,6 +56,19 @@ tracking_allocator_clear :: proc(t: ^Tracking_Allocator) {
sync.mutex_unlock(&t.mutex) sync.mutex_unlock(&t.mutex)
} }
// Reset all of a Tracking Allocator's allocation data back to zero.
tracking_allocator_reset :: proc(t: ^Tracking_Allocator) {
sync.mutex_lock(&t.mutex)
clear(&t.allocation_map)
clear(&t.bad_free_array)
t.total_memory_allocated = 0
t.total_allocation_count = 0
t.total_memory_freed = 0
t.total_free_count = 0
t.peak_memory_allocated = 0
t.current_memory_allocated = 0
sync.mutex_unlock(&t.mutex)
}
@(require_results) @(require_results)
tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator { tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
+1 -1
View File
@@ -442,7 +442,7 @@ F_GETPATH :: 50 // return the full path of the fd
foreign libc { foreign libc {
@(link_name="__error") __error :: proc() -> ^c.int --- @(link_name="__error") __error :: proc() -> ^c.int ---
@(link_name="open") _unix_open :: proc(path: cstring, flags: i32, mode: u16) -> Handle --- @(link_name="open") _unix_open :: proc(path: cstring, flags: i32, #c_vararg args: ..any) -> Handle ---
@(link_name="close") _unix_close :: proc(handle: Handle) -> c.int --- @(link_name="close") _unix_close :: proc(handle: Handle) -> c.int ---
@(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int --- @(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int ---
@(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int --- @(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int ---
+4 -4
View File
@@ -350,9 +350,9 @@ Output:
ab ab
*/ */
write_byte :: proc(b: ^Builder, x: byte) -> (n: int) { write_byte :: proc(b: ^Builder, x: byte, loc := #caller_location) -> (n: int) {
n0 := len(b.buf) n0 := len(b.buf)
append(&b.buf, x) append(&b.buf, x, loc)
n1 := len(b.buf) n1 := len(b.buf)
return n1-n0 return n1-n0
} }
@@ -380,9 +380,9 @@ NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n`
Returns: Returns:
- n: The number of bytes appended - n: The number of bytes appended
*/ */
write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) { write_bytes :: proc(b: ^Builder, x: []byte, loc := #caller_location) -> (n: int) {
n0 := len(b.buf) n0 := len(b.buf)
append(&b.buf, ..x) append(&b.buf, ..x, loc=loc)
n1 := len(b.buf) n1 := len(b.buf)
return n1-n0 return n1-n0
} }
+2 -2
View File
@@ -95,7 +95,7 @@ sem_t :: struct {
PTHREAD_CANCEL_ENABLE :: 0 PTHREAD_CANCEL_ENABLE :: 0
PTHREAD_CANCEL_DISABLE :: 1 PTHREAD_CANCEL_DISABLE :: 1
PTHREAD_CANCEL_DEFERRED :: 0 PTHREAD_CANCEL_DEFERRED :: 0
PTHREAD_CANCEL_ASYNCHRONOUS :: 1 PTHREAD_CANCEL_ASYNCHRONOUS :: 2
foreign import "system:pthread" foreign import "system:pthread"
@@ -119,4 +119,4 @@ foreign pthread {
pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int --- pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int ---
pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int --- pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int ---
pthread_cancel :: proc (thread: pthread_t) -> c.int --- pthread_cancel :: proc (thread: pthread_t) -> c.int ---
} }
+2 -2
View File
@@ -49,7 +49,7 @@ sem_t :: distinct rawptr
PTHREAD_CANCEL_ENABLE :: 0 PTHREAD_CANCEL_ENABLE :: 0
PTHREAD_CANCEL_DISABLE :: 1 PTHREAD_CANCEL_DISABLE :: 1
PTHREAD_CANCEL_DEFERRED :: 0 PTHREAD_CANCEL_DEFERRED :: 0
PTHREAD_CANCEL_ASYNCHRONOUS :: 1 PTHREAD_CANCEL_ASYNCHRONOUS :: 2
foreign import libc "system:c" foreign import libc "system:c"
@@ -71,4 +71,4 @@ foreign libc {
pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int --- pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int ---
pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int --- pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int ---
pthread_cancel :: proc (thread: pthread_t) -> c.int --- pthread_cancel :: proc (thread: pthread_t) -> c.int ---
} }
+1
View File
@@ -116,4 +116,5 @@ foreign pthread {
pthread_mutexattr_setpshared :: proc(attrs: ^pthread_mutexattr_t, value: c.int) -> c.int --- pthread_mutexattr_setpshared :: proc(attrs: ^pthread_mutexattr_t, value: c.int) -> c.int ---
pthread_mutexattr_getpshared :: proc(attrs: ^pthread_mutexattr_t, result: ^c.int) -> c.int --- pthread_mutexattr_getpshared :: proc(attrs: ^pthread_mutexattr_t, result: ^c.int) -> c.int ---
pthread_testcancel :: proc () ---
} }
+48
View File
@@ -0,0 +1,48 @@
//+private
package testing
import "base:runtime"
import "core:sync/chan"
import "core:time"
Test_State :: enum {
Ready,
Running,
Successful,
Failed,
}
Update_Channel :: chan.Chan(Channel_Event)
Update_Channel_Sender :: chan.Chan(Channel_Event, .Send)
Task_Channel :: struct {
channel: Update_Channel,
test_index: int,
}
Event_New_Test :: struct {
test_index: int,
}
Event_State_Change :: struct {
new_state: Test_State,
}
Event_Set_Fail_Timeout :: struct {
at_time: time.Time,
location: runtime.Source_Code_Location,
}
Event_Log_Message :: struct {
level: runtime.Logger_Level,
text: string,
time: time.Time,
formatted_text: string,
}
Channel_Event :: union {
Event_New_Test,
Event_State_Change,
Event_Set_Fail_Timeout,
Event_Log_Message,
}
+71
View File
@@ -0,0 +1,71 @@
//+private
package testing
import "base:runtime"
import "core:fmt"
import pkg_log "core:log"
import "core:strings"
import "core:sync/chan"
import "core:time"
Default_Test_Logger_Opts :: runtime.Logger_Options {
.Level,
.Terminal_Color,
.Short_File_Path,
.Line,
.Procedure,
.Date, .Time,
}
Log_Message :: struct {
level: runtime.Logger_Level,
text: string,
time: time.Time,
// `text` may be allocated differently, depending on where a log message
// originates from.
allocator: runtime.Allocator,
}
test_logger_proc :: proc(logger_data: rawptr, level: runtime.Logger_Level, text: string, options: runtime.Logger_Options, location := #caller_location) {
t := cast(^T)logger_data
if level >= .Error {
t.error_count += 1
}
cloned_text, clone_error := strings.clone(text, t._log_allocator)
assert(clone_error == nil, "Error while cloning string in test thread logger proc.")
now := time.now()
chan.send(t.channel, Event_Log_Message {
level = level,
text = cloned_text,
time = now,
formatted_text = format_log_text(level, text, options, location, now, t._log_allocator),
})
}
runner_logger_proc :: proc(logger_data: rawptr, level: runtime.Logger_Level, text: string, options: runtime.Logger_Options, location := #caller_location) {
log_messages := cast(^[dynamic]Log_Message)logger_data
now := time.now()
append(log_messages, Log_Message {
level = level,
text = format_log_text(level, text, options, location, now),
time = now,
allocator = context.allocator,
})
}
format_log_text :: proc(level: runtime.Logger_Level, text: string, options: runtime.Logger_Options, location: runtime.Source_Code_Location, at_time: time.Time, allocator := context.allocator) -> string{
backing: [1024]byte
buf := strings.builder_from_bytes(backing[:])
pkg_log.do_level_header(options, &buf, level)
pkg_log.do_time_header(options, &buf, at_time)
pkg_log.do_location_header(options, &buf, location)
return fmt.aprintf("%s%s", strings.to_string(buf), text, allocator = allocator)
}
+329
View File
@@ -0,0 +1,329 @@
//+private
package testing
import "base:runtime"
import "core:encoding/ansi"
import "core:fmt"
import "core:io"
import "core:mem"
import "core:path/filepath"
import "core:strings"
// Definitions of colors for use in the test runner.
SGR_RESET :: ansi.CSI + ansi.RESET + ansi.SGR
SGR_READY :: ansi.CSI + ansi.FG_BRIGHT_BLACK + ansi.SGR
SGR_RUNNING :: ansi.CSI + ansi.FG_YELLOW + ansi.SGR
SGR_SUCCESS :: ansi.CSI + ansi.FG_GREEN + ansi.SGR
SGR_FAILED :: ansi.CSI + ansi.FG_RED + ansi.SGR
MAX_PROGRESS_WIDTH :: 100
// More than enough bytes to cover long package names, long test names, dozens
// of ANSI codes, et cetera.
LINE_BUFFER_SIZE :: (MAX_PROGRESS_WIDTH * 8 + 224) * runtime.Byte
PROGRESS_COLUMN_SPACING :: 2
Package_Run :: struct {
name: string,
header: string,
frame_ready: bool,
redraw_buffer: [LINE_BUFFER_SIZE]byte,
redraw_string: string,
last_change_state: Test_State,
last_change_name: string,
tests: []Internal_Test,
test_states: []Test_State,
}
Report :: struct {
packages: []Package_Run,
packages_by_name: map[string]^Package_Run,
pkg_column_len: int,
test_column_len: int,
progress_width: int,
all_tests: []Internal_Test,
all_test_states: []Test_State,
}
// Organize all tests by package and sort out test state data.
make_report :: proc(internal_tests: []Internal_Test) -> (report: Report, error: runtime.Allocator_Error) {
assert(len(internal_tests) > 0, "make_report called with no tests")
packages: [dynamic]Package_Run
report.all_tests = internal_tests
report.all_test_states = make([]Test_State, len(internal_tests)) or_return
// First, figure out what belongs where.
#no_bounds_check cur_pkg := internal_tests[0].pkg
pkg_start: int
// This loop assumes the tests are sorted by package already.
for it, index in internal_tests {
if cur_pkg != it.pkg {
#no_bounds_check {
append(&packages, Package_Run {
name = cur_pkg,
tests = report.all_tests[pkg_start:index],
test_states = report.all_test_states[pkg_start:index],
}) or_return
}
when PROGRESS_WIDTH == 0 {
report.progress_width = max(report.progress_width, index - pkg_start)
}
pkg_start = index
report.pkg_column_len = max(report.pkg_column_len, len(cur_pkg))
cur_pkg = it.pkg
}
report.test_column_len = max(report.test_column_len, len(it.name))
}
// Handle the last (or only) package.
#no_bounds_check {
append(&packages, Package_Run {
name = cur_pkg,
header = cur_pkg,
tests = report.all_tests[pkg_start:],
test_states = report.all_test_states[pkg_start:],
}) or_return
}
when PROGRESS_WIDTH == 0 {
report.progress_width = max(report.progress_width, len(internal_tests) - pkg_start)
} else {
report.progress_width = PROGRESS_WIDTH
}
report.progress_width = min(report.progress_width, MAX_PROGRESS_WIDTH)
report.pkg_column_len = PROGRESS_COLUMN_SPACING + max(report.pkg_column_len, len(cur_pkg))
shrink(&packages) or_return
for &pkg in packages {
pkg.header = fmt.aprintf("%- *[1]s[", pkg.name, report.pkg_column_len)
assert(len(pkg.header) > 0, "Error allocating package header string.")
// This is safe because the array is done resizing, and it has the same
// lifetime as the map.
report.packages_by_name[pkg.name] = &pkg
}
// It's okay to discard the dynamic array's allocator information here,
// because its capacity has been shrunk to its length, it was allocated by
// the caller's context allocator, and it will be deallocated by the same.
//
// `delete_slice` is equivalent to `delete_dynamic_array` in this case.
report.packages = packages[:]
return
}
destroy_report :: proc(report: ^Report) {
for pkg in report.packages {
delete(pkg.header)
}
delete(report.packages)
delete(report.packages_by_name)
delete(report.all_test_states)
}
redraw_package :: proc(w: io.Writer, report: Report, pkg: ^Package_Run) {
if pkg.frame_ready {
io.write_string(w, pkg.redraw_string)
return
}
// Write the output line here so we can cache it.
line_builder := strings.builder_from_bytes(pkg.redraw_buffer[:])
line_writer := strings.to_writer(&line_builder)
highest_run_index: int
failed_count: int
done_count: int
#no_bounds_check for i := 0; i < len(pkg.test_states); i += 1 {
switch pkg.test_states[i] {
case .Ready:
continue
case .Running:
highest_run_index = max(highest_run_index, i)
case .Successful:
done_count += 1
case .Failed:
failed_count += 1
done_count += 1
}
}
start := max(0, highest_run_index - (report.progress_width - 1))
end := min(start + report.progress_width, len(pkg.test_states))
// This variable is to keep track of the last ANSI code emitted, in
// order to avoid repeating the same code over in a sequence.
//
// This should help reduce screen flicker.
last_state := Test_State(-1)
io.write_string(line_writer, pkg.header)
#no_bounds_check for state in pkg.test_states[start:end] {
switch state {
case .Ready:
if last_state != state {
io.write_string(line_writer, SGR_READY)
last_state = state
}
case .Running:
if last_state != state {
io.write_string(line_writer, SGR_RUNNING)
last_state = state
}
case .Successful:
if last_state != state {
io.write_string(line_writer, SGR_SUCCESS)
last_state = state
}
case .Failed:
if last_state != state {
io.write_string(line_writer, SGR_FAILED)
last_state = state
}
}
io.write_byte(line_writer, '|')
}
for _ in 0 ..< report.progress_width - (end - start) {
io.write_byte(line_writer, ' ')
}
io.write_string(line_writer, SGR_RESET + "] ")
ticker: string
if done_count == len(pkg.test_states) {
ticker = "[package done]"
if failed_count > 0 {
ticker = fmt.tprintf("%s (" + SGR_FAILED + "%i" + SGR_RESET + " failed)", ticker, failed_count)
}
} else {
if len(pkg.last_change_name) == 0 {
#no_bounds_check pkg.last_change_name = pkg.tests[0].name
}
switch pkg.last_change_state {
case .Ready:
ticker = fmt.tprintf(SGR_READY + "%s" + SGR_RESET, pkg.last_change_name)
case .Running:
ticker = fmt.tprintf(SGR_RUNNING + "%s" + SGR_RESET, pkg.last_change_name)
case .Failed:
ticker = fmt.tprintf(SGR_FAILED + "%s" + SGR_RESET, pkg.last_change_name)
case .Successful:
ticker = fmt.tprintf(SGR_SUCCESS + "%s" + SGR_RESET, pkg.last_change_name)
}
}
if done_count == len(pkg.test_states) {
fmt.wprintfln(line_writer, " % 4i :: %s",
len(pkg.test_states),
ticker,
)
} else {
fmt.wprintfln(line_writer, "% 4i/% 4i :: %s",
done_count,
len(pkg.test_states),
ticker,
)
}
pkg.redraw_string = strings.to_string(line_builder)
pkg.frame_ready = true
io.write_string(w, pkg.redraw_string)
}
redraw_report :: proc(w: io.Writer, report: Report) {
// If we print a line longer than the user's terminal can handle, it may
// wrap around, shifting the progress report out of alignment.
//
// There are ways to get the current terminal width, and that would be the
// ideal way to handle this, but it would require system-specific code such
// as setting STDIN to be non-blocking in order to read the response from
// the ANSI DSR escape code, or reading environment variables.
//
// The DECAWM escape codes control whether or not the terminal will wrap
// long lines or overwrite the last visible character.
// This should be fine for now.
//
// Note that we only do this for the animated summary; log messages are
// still perfectly fine to wrap, as they're printed in their own batch,
// whereas the animation depends on each package being only on one line.
//
// Of course, if you resize your terminal while it's printing, things can
// still break...
fmt.wprint(w, ansi.CSI + ansi.DECAWM_OFF)
for &pkg in report.packages {
redraw_package(w, report, &pkg)
}
fmt.wprint(w, ansi.CSI + ansi.DECAWM_ON)
}
needs_to_redraw :: proc(report: Report) -> bool {
for pkg in report.packages {
if !pkg.frame_ready {
return true
}
}
return false
}
draw_status_bar :: proc(w: io.Writer, threads_string: string, total_done_count, total_test_count: int) {
if total_done_count == total_test_count {
// All tests are done; print a blank line to maintain the same height
// of the progress report.
fmt.wprintln(w)
} else {
fmt.wprintfln(w,
"%s % 4i/% 4i :: total",
threads_string,
total_done_count,
total_test_count)
}
}
write_memory_report :: proc(w: io.Writer, tracker: ^mem.Tracking_Allocator, pkg, name: string) {
fmt.wprintf(w,
"<% 10M/% 10M> <% 10M> (% 5i/% 5i) :: %s.%s",
tracker.current_memory_allocated,
tracker.total_memory_allocated,
tracker.peak_memory_allocated,
tracker.total_free_count,
tracker.total_allocation_count,
pkg,
name)
for ptr, entry in tracker.allocation_map {
fmt.wprintf(w,
"\n +++ leak % 10M @ %p [%s:%i:%s()]",
entry.size,
ptr,
filepath.base(entry.location.file_path),
entry.location.line,
entry.location.procedure)
}
for entry in tracker.bad_free_array {
fmt.wprintf(w,
"\n +++ bad free @ %p [%s:%i:%s()]",
entry.memory,
filepath.base(entry.location.file_path),
entry.location.line,
entry.location.procedure)
}
}
+791 -44
View File
@@ -1,73 +1,820 @@
//+private //+private
package testing package testing
import "base:intrinsics"
import "base:runtime"
import "core:bytes"
import "core:encoding/ansi"
@require import "core:encoding/base64"
import "core:fmt"
import "core:io" import "core:io"
@require import pkg_log "core:log"
import "core:mem"
import "core:os" import "core:os"
import "core:slice" import "core:slice"
@require import "core:strings"
import "core:sync/chan"
import "core:thread"
import "core:time"
reset_t :: proc(t: ^T) { // Specify how many threads to use when running tests.
clear(&t.cleanups) TEST_THREADS : int : #config(ODIN_TEST_THREADS, 0)
t.error_count = 0 // Track the memory used by each test.
TRACKING_MEMORY : bool : #config(ODIN_TEST_TRACK_MEMORY, true)
// Always report how much memory is used, even when there are no leaks or bad frees.
ALWAYS_REPORT_MEMORY : bool : #config(ODIN_TEST_ALWAYS_REPORT_MEMORY, false)
// Specify how much memory each thread allocator starts with.
PER_THREAD_MEMORY : int : #config(ODIN_TEST_THREAD_MEMORY, mem.ROLLBACK_STACK_DEFAULT_BLOCK_SIZE)
// Select a specific set of tests to run by name.
// Each test is separated by a comma and may optionally include the package name.
// This may be useful when running tests on multiple packages with `-all-packages`.
// The format is: `package.test_name,test_name_only,...`
TEST_NAMES : string : #config(ODIN_TEST_NAMES, "")
// Show the fancy animated progress report.
FANCY_OUTPUT : bool : #config(ODIN_TEST_FANCY, true)
// Copy failed tests to the clipboard when done.
USE_CLIPBOARD : bool : #config(ODIN_TEST_CLIPBOARD, false)
// How many test results to show at a time per package.
PROGRESS_WIDTH : int : #config(ODIN_TEST_PROGRESS_WIDTH, 24)
// This is the random seed that will be sent to each test.
// If it is unspecified, it will be set to the system cycle counter at startup.
SHARED_RANDOM_SEED : u64 : #config(ODIN_TEST_RANDOM_SEED, 0)
// Set the lowest log level for this test run.
LOG_LEVEL : string : #config(ODIN_TEST_LOG_LEVEL, "info")
get_log_level :: #force_inline proc() -> runtime.Logger_Level {
when ODIN_DEBUG {
// Always use .Debug in `-debug` mode.
return .Debug
} else {
when LOG_LEVEL == "debug" { return .Debug }
else when LOG_LEVEL == "info" { return .Info }
else when LOG_LEVEL == "warning" { return .Warning }
else when LOG_LEVEL == "error" { return .Error }
else when LOG_LEVEL == "fatal" { return .Fatal }
}
} }
end_t :: proc(t: ^T) { end_t :: proc(t: ^T) {
for i := len(t.cleanups)-1; i >= 0; i -= 1 { for i := len(t.cleanups)-1; i >= 0; i -= 1 {
c := t.cleanups[i] #no_bounds_check c := t.cleanups[i]
context = c.ctx
c.procedure(c.user_data) c.procedure(c.user_data)
} }
delete(t.cleanups)
t.cleanups = {}
}
Task_Data :: struct {
it: Internal_Test,
t: T,
allocator_index: int,
}
Task_Timeout :: struct {
test_index: int,
at_time: time.Time,
location: runtime.Source_Code_Location,
}
run_test_task :: proc(task: thread.Task) {
data := cast(^Task_Data)(task.data)
setup_task_signal_handler(task.user_index)
chan.send(data.t.channel, Event_New_Test {
test_index = task.user_index,
})
chan.send(data.t.channel, Event_State_Change {
new_state = .Running,
})
context.assertion_failure_proc = test_assertion_failure_proc
context.logger = {
procedure = test_logger_proc,
data = &data.t,
lowest_level = get_log_level(),
options = Default_Test_Logger_Opts,
}
free_all(context.temp_allocator)
data.it.p(&data.t)
end_t(&data.t)
new_state : Test_State = .Failed if failed(&data.t) else .Successful
chan.send(data.t.channel, Event_State_Change {
new_state = new_state,
})
} }
runner :: proc(internal_tests: []Internal_Test) -> bool { runner :: proc(internal_tests: []Internal_Test) -> bool {
stream := os.stream_from_handle(os.stdout) BATCH_BUFFER_SIZE :: 32 * mem.Kilobyte
w := io.to_writer(stream) POOL_BLOCK_SIZE :: 16 * mem.Kilobyte
CLIPBOARD_BUFFER_SIZE :: 16 * mem.Kilobyte
t := &T{} BUFFERED_EVENTS_PER_CHANNEL :: 16
t.w = w RESERVED_LOG_MESSAGES :: 64
reserve(&t.cleanups, 1024) RESERVED_TEST_FAILURES :: 64
defer delete(t.cleanups)
total_success_count := 0 ERROR_STRING_TIMEOUT : string : "Test timed out."
total_test_count := len(internal_tests) ERROR_STRING_UNKNOWN : string : "Test failed for unknown reasons."
OSC_WINDOW_TITLE : string : ansi.OSC + ansi.WINDOW_TITLE + ";Odin test runner (%i/%i)" + ansi.ST
slice.sort_by(internal_tests, proc(a, b: Internal_Test) -> bool { safe_delete_string :: proc(s: string, allocator := context.allocator) {
if a.pkg < b.pkg { // Guard against bad frees on static strings.
return true switch raw_data(s) {
case raw_data(ERROR_STRING_TIMEOUT), raw_data(ERROR_STRING_UNKNOWN):
return
case:
delete(s, allocator)
} }
return a.name < b.name }
})
prev_pkg := "" stdout := io.to_writer(os.stream_from_handle(os.stdout))
stderr := io.to_writer(os.stream_from_handle(os.stderr))
// -- Prepare test data.
alloc_error: mem.Allocator_Error
when TEST_NAMES != "" {
select_internal_tests: [dynamic]Internal_Test
defer delete(select_internal_tests)
{
index_list := TEST_NAMES
for selector in strings.split_iterator(&index_list, ",") {
// Temp allocator is fine since we just need to identify which test it's referring to.
split_selector := strings.split(selector, ".", context.temp_allocator)
found := false
switch len(split_selector) {
case 1:
// Only the test name?
#no_bounds_check name := split_selector[0]
find_test_by_name: for it in internal_tests {
if it.name == name {
found = true
_, alloc_error = append(&select_internal_tests, it)
fmt.assertf(alloc_error == nil, "Error appending to select internal tests: %v", alloc_error)
break find_test_by_name
}
}
case 2:
#no_bounds_check pkg := split_selector[0]
#no_bounds_check name := split_selector[1]
find_test_by_pkg_and_name: for it in internal_tests {
if it.pkg == pkg && it.name == name {
found = true
_, alloc_error = append(&select_internal_tests, it)
fmt.assertf(alloc_error == nil, "Error appending to select internal tests: %v", alloc_error)
break find_test_by_pkg_and_name
}
}
}
if !found {
fmt.wprintfln(stderr, "No test found for the name: %q", selector)
}
}
}
// Intentional shadow with user-specified tests.
internal_tests := select_internal_tests[:]
}
total_failure_count := 0
total_success_count := 0
total_done_count := 0
total_test_count := len(internal_tests)
when !FANCY_OUTPUT {
// This is strictly for updating the window title when the progress
// report is disabled. We're otherwise able to depend on the call to
// `needs_to_redraw`.
last_done_count := -1
}
if total_test_count == 0 {
// Exit early.
fmt.wprintln(stdout, "No tests to run.")
return true
}
for it in internal_tests { for it in internal_tests {
if it.p == nil { // NOTE(Feoramund): The old test runner skipped over tests with nil
total_test_count -= 1 // procedures, but I couldn't find any case where they occurred.
continue // This assert stands to prevent any oversight on my part.
} fmt.assertf(it.p != nil, "Test %s.%s has <nil> procedure.", it.pkg, it.name)
}
free_all(context.temp_allocator) slice.sort_by(internal_tests, proc(a, b: Internal_Test) -> bool {
reset_t(t) if a.pkg == b.pkg {
defer end_t(t) return a.name < b.name
if prev_pkg != it.pkg {
prev_pkg = it.pkg
logf(t, "[Package: %s]", it.pkg)
}
logf(t, "[Test: %s]", it.name)
run_internal_test(t, it)
if failed(t) {
logf(t, "[%s : FAILURE]", it.name)
} else { } else {
logf(t, "[%s : SUCCESS]", it.name) return a.pkg < b.pkg
total_success_count += 1 }
})
// -- Set thread count.
when TEST_THREADS == 0 {
thread_count := os.processor_core_count()
} else {
thread_count := max(1, TEST_THREADS)
}
thread_count = min(thread_count, total_test_count)
// -- Allocate.
pool_stack: mem.Rollback_Stack
alloc_error = mem.rollback_stack_init(&pool_stack, POOL_BLOCK_SIZE)
fmt.assertf(alloc_error == nil, "Error allocating memory for thread pool: %v", alloc_error)
defer mem.rollback_stack_destroy(&pool_stack)
pool: thread.Pool
thread.pool_init(&pool, mem.rollback_stack_allocator(&pool_stack), thread_count)
defer thread.pool_destroy(&pool)
task_channels: []Task_Channel = ---
task_channels, alloc_error = make([]Task_Channel, thread_count)
fmt.assertf(alloc_error == nil, "Error allocating memory for update channels: %v", alloc_error)
defer delete(task_channels)
for &task_channel, index in task_channels {
task_channel.channel, alloc_error = chan.create_buffered(Update_Channel, BUFFERED_EVENTS_PER_CHANNEL, context.allocator)
fmt.assertf(alloc_error == nil, "Error allocating memory for update channel #%i: %v", index, alloc_error)
}
defer for &task_channel in task_channels {
chan.destroy(&task_channel.channel)
}
// This buffer is used to batch writes to STDOUT or STDERR, to help reduce
// screen flickering.
batch_buffer: bytes.Buffer
bytes.buffer_init_allocator(&batch_buffer, 0, BATCH_BUFFER_SIZE)
batch_writer := io.to_writer(bytes.buffer_to_stream(&batch_buffer))
defer bytes.buffer_destroy(&batch_buffer)
report: Report = ---
report, alloc_error = make_report(internal_tests)
fmt.assertf(alloc_error == nil, "Error allocating memory for test report: %v", alloc_error)
defer destroy_report(&report)
when FANCY_OUTPUT {
// We cannot make use of the ANSI save/restore cursor codes, because they
// work by absolute screen coordinates. This will cause unnecessary
// scrollback if we print at the bottom of someone's terminal.
ansi_redraw_string := fmt.aprintf(
// ANSI for "go up N lines then erase the screen from the cursor forward."
ansi.CSI + "%i" + ansi.CPL + ansi.CSI + ansi.ED +
// We'll combine this with the window title format string, since it
// can be printed at the same time.
"%s",
// 1 extra line for the status bar.
1 + len(report.packages), OSC_WINDOW_TITLE)
assert(len(ansi_redraw_string) > 0, "Error allocating ANSI redraw string.")
defer delete(ansi_redraw_string)
thread_count_status_string: string = ---
{
PADDING :: PROGRESS_COLUMN_SPACING + PROGRESS_WIDTH
unpadded := fmt.tprintf("%i thread%s", thread_count, "" if thread_count == 1 else "s")
thread_count_status_string = fmt.aprintf("%- *[1]s", unpadded, report.pkg_column_len + PADDING)
assert(len(thread_count_status_string) > 0, "Error allocating thread count status string.")
}
defer delete(thread_count_status_string)
}
task_data_slots: []Task_Data = ---
task_data_slots, alloc_error = make([]Task_Data, thread_count)
fmt.assertf(alloc_error == nil, "Error allocating memory for task data slots: %v", alloc_error)
defer delete(task_data_slots)
// Tests rotate through these allocators as they finish.
task_allocators: []mem.Rollback_Stack = ---
task_allocators, alloc_error = make([]mem.Rollback_Stack, thread_count)
fmt.assertf(alloc_error == nil, "Error allocating memory for task allocators: %v", alloc_error)
defer delete(task_allocators)
when TRACKING_MEMORY {
task_memory_trackers: []mem.Tracking_Allocator = ---
task_memory_trackers, alloc_error = make([]mem.Tracking_Allocator, thread_count)
fmt.assertf(alloc_error == nil, "Error allocating memory for memory trackers: %v", alloc_error)
defer delete(task_memory_trackers)
}
#no_bounds_check for i in 0 ..< thread_count {
alloc_error = mem.rollback_stack_init(&task_allocators[i], PER_THREAD_MEMORY)
fmt.assertf(alloc_error == nil, "Error allocating memory for task allocator #%i: %v", i, alloc_error)
when TRACKING_MEMORY {
mem.tracking_allocator_init(&task_memory_trackers[i], mem.rollback_stack_allocator(&task_allocators[i]))
} }
} }
logf(t, "----------------------------------------")
if total_test_count == 0 { defer #no_bounds_check for i in 0 ..< thread_count {
log(t, "NO TESTS RAN") when TRACKING_MEMORY {
} else { mem.tracking_allocator_destroy(&task_memory_trackers[i])
logf(t, "%d/%d SUCCESSFUL", total_success_count, total_test_count) }
mem.rollback_stack_destroy(&task_allocators[i])
} }
task_timeouts: [dynamic]Task_Timeout = ---
task_timeouts, alloc_error = make([dynamic]Task_Timeout, 0, thread_count)
fmt.assertf(alloc_error == nil, "Error allocating memory for task timeouts: %v", alloc_error)
defer delete(task_timeouts)
failed_test_reason_map: map[int]string = ---
failed_test_reason_map, alloc_error = make(map[int]string, RESERVED_TEST_FAILURES)
fmt.assertf(alloc_error == nil, "Error allocating memory for failed test reasons: %v", alloc_error)
defer delete(failed_test_reason_map)
log_messages: [dynamic]Log_Message = ---
log_messages, alloc_error = make([dynamic]Log_Message, 0, RESERVED_LOG_MESSAGES)
fmt.assertf(alloc_error == nil, "Error allocating memory for log message queue: %v", alloc_error)
defer delete(log_messages)
sorted_failed_test_reasons: [dynamic]int = ---
sorted_failed_test_reasons, alloc_error = make([dynamic]int, 0, RESERVED_TEST_FAILURES)
fmt.assertf(alloc_error == nil, "Error allocating memory for sorted failed test reasons: %v", alloc_error)
defer delete(sorted_failed_test_reasons)
when USE_CLIPBOARD {
clipboard_buffer: bytes.Buffer
bytes.buffer_init_allocator(&clipboard_buffer, 0, CLIPBOARD_BUFFER_SIZE)
defer bytes.buffer_destroy(&clipboard_buffer)
}
when SHARED_RANDOM_SEED == 0 {
shared_random_seed := cast(u64)intrinsics.read_cycle_counter()
} else {
shared_random_seed := SHARED_RANDOM_SEED
}
// -- Setup initial tasks.
// NOTE(Feoramund): This is the allocator that will be used by threads to
// persist log messages past their lifetimes. It has its own variable name
// in the event it needs to be changed from `context.allocator` without
// digging through the source to divine everywhere it is used for that.
shared_log_allocator := context.allocator
context.logger = {
procedure = runner_logger_proc,
data = &log_messages,
lowest_level = get_log_level(),
options = Default_Test_Logger_Opts - {.Short_File_Path, .Line, .Procedure},
}
run_index: int
setup_tasks: for &data, task_index in task_data_slots {
setup_next_test: for run_index < total_test_count {
#no_bounds_check it := internal_tests[run_index]
defer run_index += 1
data.it = it
data.t.seed = shared_random_seed
#no_bounds_check data.t.channel = chan.as_send(task_channels[task_index].channel)
data.t._log_allocator = shared_log_allocator
data.allocator_index = task_index
#no_bounds_check when TRACKING_MEMORY {
task_allocator := mem.tracking_allocator(&task_memory_trackers[task_index])
} else {
task_allocator := mem.rollback_stack_allocator(&task_allocators[task_index])
}
thread.pool_add_task(&pool, task_allocator, run_test_task, &data, run_index)
continue setup_tasks
}
}
// -- Run tests.
setup_signal_handler()
fmt.wprint(stdout, ansi.CSI + ansi.DECTCEM_HIDE)
when FANCY_OUTPUT {
signals_were_raised := false
redraw_report(stdout, report)
draw_status_bar(stdout, thread_count_status_string, total_done_count, total_test_count)
}
when TEST_THREADS == 0 {
pkg_log.infof("Starting test runner with %i thread%s. Set with -define:ODIN_TEST_THREADS=n.",
thread_count,
"" if thread_count == 1 else "s")
} else {
pkg_log.infof("Starting test runner with %i thread%s.",
thread_count,
"" if thread_count == 1 else "s")
}
when SHARED_RANDOM_SEED == 0 {
pkg_log.infof("The random seed sent to every test is: %v. Set with -define:ODIN_TEST_RANDOM_SEED=n.", shared_random_seed)
} else {
pkg_log.infof("The random seed sent to every test is: %v.", shared_random_seed)
}
when TRACKING_MEMORY {
when ALWAYS_REPORT_MEMORY {
pkg_log.info("Memory tracking is enabled. Tests will log their memory usage when complete.")
} else {
pkg_log.info("Memory tracking is enabled. Tests will log their memory usage if there's an issue.")
}
pkg_log.info("< Final Mem/ Total Mem> < Peak Mem> (#Free/Alloc) :: [package.test_name]")
} else when ALWAYS_REPORT_MEMORY {
pkg_log.warn("ODIN_TEST_ALWAYS_REPORT_MEMORY is true, but ODIN_TRACK_MEMORY is false.")
}
start_time := time.now()
thread.pool_start(&pool)
main_loop: for !thread.pool_is_empty(&pool) {
{
events_pending := thread.pool_num_done(&pool) > 0
if !events_pending {
poll_tasks: for &task_channel in task_channels {
if chan.len(task_channel.channel) > 0 {
events_pending = true
break poll_tasks
}
}
}
if !events_pending {
// Keep the main thread from pegging a core at 100% usage.
time.sleep(1 * time.Microsecond)
}
}
cycle_pool: for task in thread.pool_pop_done(&pool) {
data := cast(^Task_Data)(task.data)
when TRACKING_MEMORY {
#no_bounds_check tracker := &task_memory_trackers[data.allocator_index]
memory_is_in_bad_state := len(tracker.allocation_map) + len(tracker.bad_free_array) > 0
when ALWAYS_REPORT_MEMORY {
should_report := true
} else {
should_report := memory_is_in_bad_state
}
if should_report {
write_memory_report(batch_writer, tracker, data.it.pkg, data.it.name)
pkg_log.log(.Warning if memory_is_in_bad_state else .Info, bytes.buffer_to_string(&batch_buffer))
bytes.buffer_reset(&batch_buffer)
}
mem.tracking_allocator_reset(tracker)
}
free_all(task.allocator)
if run_index < total_test_count {
#no_bounds_check it := internal_tests[run_index]
defer run_index += 1
data.it = it
data.t.seed = shared_random_seed
data.t.error_count = 0
thread.pool_add_task(&pool, task.allocator, run_test_task, data, run_index)
}
}
handle_events: for &task_channel in task_channels {
for ev in chan.try_recv(task_channel.channel) {
switch event in ev {
case Event_New_Test:
task_channel.test_index = event.test_index
case Event_State_Change:
#no_bounds_check report.all_test_states[task_channel.test_index] = event.new_state
#no_bounds_check it := internal_tests[task_channel.test_index]
#no_bounds_check pkg := report.packages_by_name[it.pkg]
#partial switch event.new_state {
case .Failed:
if task_channel.test_index not_in failed_test_reason_map {
failed_test_reason_map[task_channel.test_index] = ERROR_STRING_UNKNOWN
}
total_failure_count += 1
total_done_count += 1
case .Successful:
total_success_count += 1
total_done_count += 1
}
when ODIN_DEBUG {
pkg_log.debugf("Test #%i %s.%s changed state to %v.", task_channel.test_index, it.pkg, it.name, event.new_state)
}
pkg.last_change_state = event.new_state
pkg.last_change_name = it.name
pkg.frame_ready = false
case Event_Set_Fail_Timeout:
_, alloc_error = append(&task_timeouts, Task_Timeout {
test_index = task_channel.test_index,
at_time = event.at_time,
location = event.location,
})
fmt.assertf(alloc_error == nil, "Error appending to task timeouts: %v", alloc_error)
case Event_Log_Message:
_, alloc_error = append(&log_messages, Log_Message {
level = event.level,
text = event.formatted_text,
time = event.time,
allocator = shared_log_allocator,
})
fmt.assertf(alloc_error == nil, "Error appending to log messages: %v", alloc_error)
if event.level >= .Error {
// Save the message for the final summary.
if old_error, ok := failed_test_reason_map[task_channel.test_index]; ok {
safe_delete_string(old_error, shared_log_allocator)
}
failed_test_reason_map[task_channel.test_index] = event.text
} else {
delete(event.text, shared_log_allocator)
}
}
}
}
check_timeouts: for i := len(task_timeouts) - 1; i >= 0; i -= 1 {
#no_bounds_check timeout := &task_timeouts[i]
if time.since(timeout.at_time) < 0 {
continue check_timeouts
}
defer unordered_remove(&task_timeouts, i)
#no_bounds_check if report.all_test_states[timeout.test_index] > .Running {
continue check_timeouts
}
if !thread.pool_stop_task(&pool, timeout.test_index) {
// The task may have stopped a split second after we started
// checking, but we haven't handled the new state yet.
continue check_timeouts
}
#no_bounds_check report.all_test_states[timeout.test_index] = .Failed
#no_bounds_check it := internal_tests[timeout.test_index]
#no_bounds_check pkg := report.packages_by_name[it.pkg]
pkg.frame_ready = false
if old_error, ok := failed_test_reason_map[timeout.test_index]; ok {
safe_delete_string(old_error, shared_log_allocator)
}
failed_test_reason_map[timeout.test_index] = ERROR_STRING_TIMEOUT
total_failure_count += 1
total_done_count += 1
now := time.now()
_, alloc_error = append(&log_messages, Log_Message {
level = .Error,
text = format_log_text(.Error, ERROR_STRING_TIMEOUT, Default_Test_Logger_Opts, timeout.location, now),
time = now,
allocator = context.allocator,
})
fmt.assertf(alloc_error == nil, "Error appending to log messages: %v", alloc_error)
find_task_data: for &data in task_data_slots {
if data.it.pkg == it.pkg && data.it.name == it.name {
end_t(&data.t)
break find_task_data
}
}
}
if should_stop_runner() {
fmt.wprintln(stderr, "\nCaught interrupt signal. Stopping all tests.")
thread.pool_shutdown(&pool)
break main_loop
}
when FANCY_OUTPUT {
// Because the bounds checking procs send directly to STDERR with
// no way to redirect or handle them, we need to at least try to
// let the user see those messages when using the animated progress
// report. This flag may be set by the block of code below if a
// signal is raised.
//
// It'll be purely by luck if the output is interleaved properly,
// given the nature of non-thread-safe printing.
//
// At worst, if Odin did not print any error for this signal, we'll
// just re-display the progress report. The fatal log error message
// should be enough to clue the user in that something dire has
// occurred.
bypass_progress_overwrite := false
}
if test_index, reason, ok := should_stop_test(); ok {
#no_bounds_check report.all_test_states[test_index] = .Failed
#no_bounds_check it := internal_tests[test_index]
#no_bounds_check pkg := report.packages_by_name[it.pkg]
pkg.frame_ready = false
fmt.assertf(thread.pool_stop_task(&pool, test_index),
"A signal (%v) was raised to stop test #%i %s.%s, but it was unable to be found.",
reason, test_index, it.pkg, it.name)
if test_index not_in failed_test_reason_map {
// We only write a new error message here if there wasn't one
// already, because the message we can provide based only on
// the signal won't be very useful, whereas asserts and panics
// will provide a user-written error message.
failed_test_reason_map[test_index] = fmt.aprintf("Signal caught: %v", reason, allocator = shared_log_allocator)
pkg_log.fatalf("Caught signal to stop test #%i %s.%s for: %v.", test_index, it.pkg, it.name, reason)
}
when FANCY_OUTPUT {
bypass_progress_overwrite = true
signals_were_raised = true
}
total_failure_count += 1
total_done_count += 1
}
// -- Redraw.
when FANCY_OUTPUT {
if len(log_messages) == 0 && !needs_to_redraw(report) {
continue main_loop
}
if !bypass_progress_overwrite {
fmt.wprintf(stdout, ansi_redraw_string, total_done_count, total_test_count)
}
} else {
if total_done_count != last_done_count {
fmt.wprintf(stdout, OSC_WINDOW_TITLE, total_done_count, total_test_count)
last_done_count = total_done_count
}
if len(log_messages) == 0 {
continue main_loop
}
}
// Because each thread has its own messenger channel, log messages
// arrive in chunks that are in-order, but when they're merged with the
// logs from other threads, they become out-of-order.
slice.stable_sort_by(log_messages[:], proc(a, b: Log_Message) -> bool {
return time.diff(a.time, b.time) > 0
})
for message in log_messages {
fmt.wprintln(batch_writer, message.text)
delete(message.text, message.allocator)
}
fmt.wprint(stderr, bytes.buffer_to_string(&batch_buffer))
clear(&log_messages)
bytes.buffer_reset(&batch_buffer)
when FANCY_OUTPUT {
redraw_report(batch_writer, report)
draw_status_bar(batch_writer, thread_count_status_string, total_done_count, total_test_count)
fmt.wprint(stdout, bytes.buffer_to_string(&batch_buffer))
bytes.buffer_reset(&batch_buffer)
}
}
// -- All tests are complete, or the runner has been interrupted.
// NOTE(Feoramund): If you've arrived here after receiving signal 11 or
// SIGSEGV on the main runner thread, while using a UNIX-like platform,
// there is the possibility that you may have encountered a rare edge case
// involving the joining of threads.
//
// At the time of writing, the thread library is undergoing a rewrite that
// should solve this problem; it is not an issue with the test runner itself.
thread.pool_join(&pool)
finished_in := time.since(start_time)
when !FANCY_OUTPUT {
// One line to space out the results, since we don't have the status
// bar in plain mode.
fmt.wprintln(batch_writer)
}
fmt.wprintf(batch_writer,
"Finished %i test%s in %v.",
total_done_count,
"" if total_done_count == 1 else "s",
finished_in)
if total_done_count != total_test_count {
not_run_count := total_test_count - total_done_count
fmt.wprintf(batch_writer,
" " + SGR_READY + "%i" + SGR_RESET + " %s left undone.",
not_run_count,
"test was" if not_run_count == 1 else "tests were")
}
if total_success_count == total_test_count {
fmt.wprintfln(batch_writer,
" %s " + SGR_SUCCESS + "successful." + SGR_RESET,
"The test was" if total_test_count == 1 else "All tests were")
} else if total_failure_count > 0 {
if total_failure_count == total_test_count {
fmt.wprintfln(batch_writer,
" %s " + SGR_FAILED + "failed." + SGR_RESET,
"The test" if total_test_count == 1 else "All tests")
} else {
fmt.wprintfln(batch_writer,
" " + SGR_FAILED + "%i" + SGR_RESET + " test%s failed.",
total_failure_count,
"" if total_failure_count == 1 else "s")
}
for test_index in failed_test_reason_map {
_, alloc_error = append(&sorted_failed_test_reasons, test_index)
fmt.assertf(alloc_error == nil, "Error appending to sorted failed test reasons: %v", alloc_error)
}
slice.sort(sorted_failed_test_reasons[:])
for test_index in sorted_failed_test_reasons {
#no_bounds_check last_error := failed_test_reason_map[test_index]
#no_bounds_check it := internal_tests[test_index]
pkg_and_name := fmt.tprintf("%s.%s", it.pkg, it.name)
fmt.wprintfln(batch_writer, " - %- *[1]s\t%s",
pkg_and_name,
report.pkg_column_len + report.test_column_len,
last_error)
safe_delete_string(last_error, shared_log_allocator)
}
if total_success_count > 0 {
when USE_CLIPBOARD {
clipboard_writer := io.to_writer(bytes.buffer_to_stream(&clipboard_buffer))
fmt.wprint(clipboard_writer, "-define:ODIN_TEST_NAMES=")
for test_index in sorted_failed_test_reasons {
#no_bounds_check it := internal_tests[test_index]
fmt.wprintf(clipboard_writer, "%s.%s,", it.pkg, it.name)
}
encoded_names := base64.encode(bytes.buffer_to_bytes(&clipboard_buffer), allocator = context.temp_allocator)
fmt.wprintf(batch_writer,
ansi.OSC + ansi.CLIPBOARD + ";c;%s" + ansi.ST +
"\nThe name%s of the failed test%s been copied to your clipboard.",
encoded_names,
"" if total_failure_count == 1 else "s",
" has" if total_failure_count == 1 else "s have")
} else {
fmt.wprintf(batch_writer, "\nTo run only the failed test%s, use:\n\t-define:ODIN_TEST_NAMES=",
"" if total_failure_count == 1 else "s")
for test_index in sorted_failed_test_reasons {
#no_bounds_check it := internal_tests[test_index]
fmt.wprintf(batch_writer, "%s.%s,", it.pkg, it.name)
}
fmt.wprint(batch_writer, "\n\nIf your terminal supports OSC 52, you may use -define:ODIN_TEST_CLIPBOARD to have this copied directly to your clipboard.")
}
fmt.wprintln(batch_writer)
}
}
fmt.wprint(stdout, ansi.CSI + ansi.DECTCEM_SHOW)
when FANCY_OUTPUT {
if signals_were_raised {
fmt.wprintln(batch_writer, `
Signals were raised during this test run. Log messages are likely to have collided with each other.
To partly mitigate this, redirect STDERR to a file or use the -define:ODIN_TEST_FANCY=false option.`)
}
}
fmt.wprintln(stderr, bytes.buffer_to_string(&batch_buffer))
return total_success_count == total_test_count return total_success_count == total_test_count
} }
-14
View File
@@ -1,14 +0,0 @@
//+private
//+build !windows
package testing
import "core:time"
run_internal_test :: proc(t: ^T, it: Internal_Test) {
// TODO(bill): Catch panics on other platforms
it.p(t)
}
_fail_timeout :: proc(t: ^T, duration: time.Duration, loc := #caller_location) {
}
-235
View File
@@ -1,235 +0,0 @@
//+private
//+build windows
package testing
import win32 "core:sys/windows"
import "base:runtime"
import "base:intrinsics"
import "core:time"
Sema :: struct {
count: i32,
}
sema_reset :: proc "contextless" (s: ^Sema) {
intrinsics.atomic_store(&s.count, 0)
}
sema_wait :: proc "contextless" (s: ^Sema) {
for {
original_count := s.count
for original_count == 0 {
win32.WaitOnAddress(&s.count, &original_count, size_of(original_count), win32.INFINITE)
original_count = s.count
}
if original_count == intrinsics.atomic_compare_exchange_strong(&s.count, original_count-1, original_count) {
return
}
}
}
sema_wait_with_timeout :: proc "contextless" (s: ^Sema, duration: time.Duration) -> bool {
if duration <= 0 {
return false
}
for {
original_count := intrinsics.atomic_load(&s.count)
for start := time.tick_now(); original_count == 0; /**/ {
if intrinsics.atomic_load(&s.count) != original_count {
remaining := duration - time.tick_since(start)
if remaining < 0 {
return false
}
ms := u32(remaining/time.Millisecond)
if !win32.WaitOnAddress(&s.count, &original_count, size_of(original_count), ms) {
return false
}
}
original_count = s.count
}
if original_count == intrinsics.atomic_compare_exchange_strong(&s.count, original_count-1, original_count) {
return true
}
}
}
sema_post :: proc "contextless" (s: ^Sema, count := 1) {
intrinsics.atomic_add(&s.count, i32(count))
if count == 1 {
win32.WakeByAddressSingle(&s.count)
} else {
win32.WakeByAddressAll(&s.count)
}
}
Thread_Proc :: #type proc(^Thread)
MAX_USER_ARGUMENTS :: 8
Thread :: struct {
using specific: Thread_Os_Specific,
procedure: Thread_Proc,
t: ^T,
it: Internal_Test,
success: bool,
init_context: Maybe(runtime.Context),
creation_allocator: runtime.Allocator,
internal_fail_timeout: time.Duration,
internal_fail_timeout_loc: runtime.Source_Code_Location,
}
Thread_Os_Specific :: struct {
win32_thread: win32.HANDLE,
win32_thread_id: win32.DWORD,
done: bool, // see note in `is_done`
}
thread_create :: proc(procedure: Thread_Proc) -> ^Thread {
__windows_thread_entry_proc :: proc "system" (t_: rawptr) -> win32.DWORD {
t := (^Thread)(t_)
context = t.init_context.? or_else runtime.default_context()
t.procedure(t)
if t.init_context == nil {
if context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
}
}
intrinsics.atomic_store(&t.done, true)
return 0
}
thread := new(Thread)
if thread == nil {
return nil
}
thread.creation_allocator = context.allocator
win32_thread_id: win32.DWORD
win32_thread := win32.CreateThread(nil, 0, __windows_thread_entry_proc, thread, win32.CREATE_SUSPENDED, &win32_thread_id)
if win32_thread == nil {
free(thread, thread.creation_allocator)
return nil
}
thread.procedure = procedure
thread.win32_thread = win32_thread
thread.win32_thread_id = win32_thread_id
thread.init_context = context
return thread
}
thread_start :: proc "contextless" (thread: ^Thread) {
win32.ResumeThread(thread.win32_thread)
}
thread_join_and_destroy :: proc(thread: ^Thread) {
if thread.win32_thread != win32.INVALID_HANDLE {
win32.WaitForSingleObject(thread.win32_thread, win32.INFINITE)
win32.CloseHandle(thread.win32_thread)
thread.win32_thread = win32.INVALID_HANDLE
}
free(thread, thread.creation_allocator)
}
thread_terminate :: proc "contextless" (thread: ^Thread, exit_code: int) {
win32.TerminateThread(thread.win32_thread, u32(exit_code))
}
_fail_timeout :: proc(t: ^T, duration: time.Duration, loc := #caller_location) {
assert(global_fail_timeout_thread == nil, "set_fail_timeout previously called", loc)
thread := thread_create(proc(thread: ^Thread) {
t := thread.t
timeout := thread.internal_fail_timeout
if !sema_wait_with_timeout(&global_fail_timeout_semaphore, timeout) {
fail_now(t, "TIMEOUT", thread.internal_fail_timeout_loc)
}
})
thread.internal_fail_timeout = duration
thread.internal_fail_timeout_loc = loc
thread.t = t
global_fail_timeout_thread = thread
thread_start(thread)
}
global_fail_timeout_thread: ^Thread
global_fail_timeout_semaphore: Sema
global_threaded_runner_semaphore: Sema
global_exception_handler: rawptr
global_current_thread: ^Thread
global_current_t: ^T
run_internal_test :: proc(t: ^T, it: Internal_Test) {
thread := thread_create(proc(thread: ^Thread) {
exception_handler_proc :: proc "system" (ExceptionInfo: ^win32.EXCEPTION_POINTERS) -> win32.LONG {
switch ExceptionInfo.ExceptionRecord.ExceptionCode {
case
win32.EXCEPTION_DATATYPE_MISALIGNMENT,
win32.EXCEPTION_BREAKPOINT,
win32.EXCEPTION_ACCESS_VIOLATION,
win32.EXCEPTION_ILLEGAL_INSTRUCTION,
win32.EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
win32.EXCEPTION_STACK_OVERFLOW:
sema_post(&global_threaded_runner_semaphore)
return win32.EXCEPTION_EXECUTE_HANDLER
}
return win32.EXCEPTION_CONTINUE_SEARCH
}
global_exception_handler = win32.AddVectoredExceptionHandler(0, exception_handler_proc)
context.assertion_failure_proc = proc(prefix, message: string, loc: runtime.Source_Code_Location) -> ! {
errorf(global_current_t, "%s %s", prefix, message, loc=loc)
intrinsics.trap()
}
t := thread.t
global_fail_timeout_thread = nil
sema_reset(&global_fail_timeout_semaphore)
thread.it.p(t)
sema_post(&global_fail_timeout_semaphore)
if global_fail_timeout_thread != nil do thread_join_and_destroy(global_fail_timeout_thread)
thread.success = true
sema_post(&global_threaded_runner_semaphore)
})
sema_reset(&global_threaded_runner_semaphore)
global_current_t = t
t._fail_now = proc() -> ! {
intrinsics.trap()
}
thread.t = t
thread.it = it
thread.success = false
thread_start(thread)
sema_wait(&global_threaded_runner_semaphore)
thread_terminate(thread, int(!thread.success))
thread_join_and_destroy(thread)
win32.RemoveVectoredExceptionHandler(global_exception_handler)
if !thread.success && t.error_count == 0 {
t.error_count += 1
}
return
}
+33
View File
@@ -0,0 +1,33 @@
//+private
package testing
import "base:runtime"
import pkg_log "core:log"
Stop_Reason :: enum {
Unknown,
Illegal_Instruction,
Arithmetic_Error,
Segmentation_Fault,
}
test_assertion_failure_proc :: proc(prefix, message: string, loc: runtime.Source_Code_Location) -> ! {
pkg_log.fatalf("%s: %s", prefix, message, location = loc)
runtime.trap()
}
setup_signal_handler :: proc() {
_setup_signal_handler()
}
setup_task_signal_handler :: proc(test_index: int) {
_setup_task_signal_handler(test_index)
}
should_stop_runner :: proc() -> bool {
return _should_stop_runner()
}
should_stop_test :: proc() -> (test_index: int, reason: Stop_Reason, ok: bool) {
return _should_stop_test()
}
+142
View File
@@ -0,0 +1,142 @@
//+private
//+build windows, linux, darwin, freebsd, openbsd, netbsd, haiku
package testing
import "base:intrinsics"
import "core:c/libc"
import "core:encoding/ansi"
import "core:sync"
@require import "core:sys/unix"
@(private="file") stop_runner_flag: libc.sig_atomic_t
@(private="file") stop_test_gate: sync.Mutex
@(private="file") stop_test_index: libc.sig_atomic_t
@(private="file") stop_test_reason: libc.sig_atomic_t
@(private="file") stop_test_alert: libc.sig_atomic_t
@(private="file", thread_local)
local_test_index: libc.sig_atomic_t
@(private="file")
stop_runner_callback :: proc "c" (sig: libc.int) {
intrinsics.atomic_store(&stop_runner_flag, 1)
}
@(private="file")
stop_test_callback :: proc "c" (sig: libc.int) {
if local_test_index == -1 {
// We're the test runner, and we ourselves have caught a signal from
// which there is no recovery.
//
// The most we can do now is make sure the user's cursor is visible,
// nuke the entire processs, and hope a useful core dump survives.
// NOTE(Feoramund): Using these write calls in a signal handler is
// undefined behavior in C99 but possibly tolerated in POSIX 2008.
// Either way, we may as well try to salvage what we can.
show_cursor := ansi.CSI + ansi.DECTCEM_SHOW
libc.fwrite(raw_data(show_cursor), size_of(byte), len(show_cursor), libc.stdout)
libc.fflush(libc.stdout)
// This is an attempt at being compliant by avoiding printf.
sigbuf: [8]byte
sigstr: string
{
signum := cast(int)sig
i := len(sigbuf) - 2
for signum > 0 {
m := signum % 10
signum /= 10
sigbuf[i] = cast(u8)('0' + m)
i -= 1
}
sigstr = cast(string)sigbuf[1 + i:len(sigbuf) - 1]
}
advisory_a := `
The test runner's main thread has caught an unrecoverable error (signal `
advisory_b := `) and will now forcibly terminate.
This is a dire bug and should be reported to the Odin developers.
`
libc.fwrite(raw_data(advisory_a), size_of(byte), len(advisory_a), libc.stderr)
libc.fwrite(raw_data(sigstr), size_of(byte), len(sigstr), libc.stderr)
libc.fwrite(raw_data(advisory_b), size_of(byte), len(advisory_b), libc.stderr)
// Try to get a core dump.
libc.abort()
}
if sync.mutex_guard(&stop_test_gate) {
intrinsics.atomic_store(&stop_test_index, local_test_index)
intrinsics.atomic_store(&stop_test_reason, cast(libc.sig_atomic_t)sig)
intrinsics.atomic_store(&stop_test_alert, 1)
for {
// Idle until this thread is terminated by the runner,
// otherwise we may continue to generate signals.
intrinsics.cpu_relax()
when ODIN_OS != .Windows {
// NOTE(Feoramund): Some UNIX-like platforms may require this.
//
// During testing, I found that NetBSD 10.0 refused to
// terminate a task thread, even when its thread had been
// properly set to PTHREAD_CANCEL_ASYNCHRONOUS.
//
// The runner would stall after returning from `pthread_cancel`.
unix.pthread_testcancel()
}
}
}
}
_setup_signal_handler :: proc() {
local_test_index = -1
// Catch user interrupt / CTRL-C.
libc.signal(libc.SIGINT, stop_runner_callback)
// Catch polite termination request.
libc.signal(libc.SIGTERM, stop_runner_callback)
// For tests:
// Catch asserts and panics.
libc.signal(libc.SIGILL, stop_test_callback)
// Catch arithmetic errors.
libc.signal(libc.SIGFPE, stop_test_callback)
// Catch segmentation faults (illegal memory access).
libc.signal(libc.SIGSEGV, stop_test_callback)
}
_setup_task_signal_handler :: proc(test_index: int) {
local_test_index = cast(libc.sig_atomic_t)test_index
}
_should_stop_runner :: proc() -> bool {
return intrinsics.atomic_load(&stop_runner_flag) == 1
}
@(private="file")
unlock_stop_test_gate :: proc(_: int, _: Stop_Reason, ok: bool) {
if ok {
sync.mutex_unlock(&stop_test_gate)
}
}
@(deferred_out=unlock_stop_test_gate)
_should_stop_test :: proc() -> (test_index: int, reason: Stop_Reason, ok: bool) {
if intrinsics.atomic_load(&stop_test_alert) == 1 {
intrinsics.atomic_store(&stop_test_alert, 0)
test_index = cast(int)intrinsics.atomic_load(&stop_test_index)
switch intrinsics.atomic_load(&stop_test_reason) {
case libc.SIGFPE: reason = .Arithmetic_Error
case libc.SIGILL: reason = .Illegal_Instruction
case libc.SIGSEGV: reason = .Segmentation_Fault
}
ok = true
}
return
}
+19
View File
@@ -0,0 +1,19 @@
//+private
//+build !windows !linux !darwin !freebsd !openbsd !netbsd !haiku
package testing
_setup_signal_handler :: proc() {
// Do nothing.
}
_setup_task_signal_handler :: proc(test_index: int) {
// Do nothing.
}
_should_stop_runner :: proc() -> bool {
return false
}
_should_stop_test :: proc() -> (test_index: int, reason: Stop_Reason, ok: bool) {
return 0, {}, false
}
+43 -26
View File
@@ -1,10 +1,11 @@
package testing package testing
import "core:fmt"
import "core:io"
import "core:time"
import "base:intrinsics" import "base:intrinsics"
import "base:runtime"
import pkg_log "core:log"
import "core:reflect" import "core:reflect"
import "core:sync/chan"
import "core:time"
_ :: reflect // alias reflect to nothing to force visibility for -vet _ :: reflect // alias reflect to nothing to force visibility for -vet
@@ -22,44 +23,55 @@ Internal_Test :: struct {
Internal_Cleanup :: struct { Internal_Cleanup :: struct {
procedure: proc(rawptr), procedure: proc(rawptr),
user_data: rawptr, user_data: rawptr,
ctx: runtime.Context,
} }
T :: struct { T :: struct {
error_count: int, error_count: int,
w: io.Writer, // If your test needs to perform random operations, it's advised to use
// this value to seed a local random number generator rather than relying
// on the non-thread-safe global one.
//
// This way, your results will be deterministic.
//
// This value is chosen at startup of the test runner, logged, and may be
// specified by the user. It is the same for all tests of a single run.
seed: u64,
channel: Update_Channel_Sender,
cleanups: [dynamic]Internal_Cleanup, cleanups: [dynamic]Internal_Cleanup,
// This allocator is shared between the test runner and its threads for
// cloning log strings, so they can outlive the lifetime of individual
// tests during channel transmission.
_log_allocator: runtime.Allocator,
_fail_now: proc() -> !, _fail_now: proc() -> !,
} }
@(deprecated="prefer `log.error`")
error :: proc(t: ^T, args: ..any, loc := #caller_location) { error :: proc(t: ^T, args: ..any, loc := #caller_location) {
fmt.wprintf(t.w, "%v: ", loc) pkg_log.error(..args, location = loc)
fmt.wprintln(t.w, ..args)
t.error_count += 1
} }
@(deprecated="prefer `log.errorf`")
errorf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) { errorf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) {
fmt.wprintf(t.w, "%v: ", loc) pkg_log.errorf(format, ..args, location = loc)
fmt.wprintf(t.w, format, ..args)
fmt.wprintln(t.w)
t.error_count += 1
} }
fail :: proc(t: ^T, loc := #caller_location) { fail :: proc(t: ^T, loc := #caller_location) {
error(t, "FAIL", loc=loc) pkg_log.error("FAIL", location=loc)
t.error_count += 1
} }
fail_now :: proc(t: ^T, msg := "", loc := #caller_location) { fail_now :: proc(t: ^T, msg := "", loc := #caller_location) {
if msg != "" { if msg != "" {
error(t, "FAIL:", msg, loc=loc) pkg_log.error("FAIL:", msg, location=loc)
} else { } else {
error(t, "FAIL", loc=loc) pkg_log.error("FAIL", location=loc)
} }
t.error_count += 1
if t._fail_now != nil { if t._fail_now != nil {
t._fail_now() t._fail_now()
} }
@@ -69,32 +81,34 @@ failed :: proc(t: ^T) -> bool {
return t.error_count != 0 return t.error_count != 0
} }
@(deprecated="prefer `log.info`")
log :: proc(t: ^T, args: ..any, loc := #caller_location) { log :: proc(t: ^T, args: ..any, loc := #caller_location) {
fmt.wprintln(t.w, ..args) pkg_log.info(..args, location = loc)
} }
@(deprecated="prefer `log.infof`")
logf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) { logf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) {
fmt.wprintf(t.w, format, ..args) pkg_log.infof(format, ..args, location = loc)
fmt.wprintln(t.w)
} }
// cleanup registers a procedure and user_data, which will be called when the test, and all its subtests, complete // cleanup registers a procedure and user_data, which will be called when the test, and all its subtests, complete.
// cleanup procedures will be called in LIFO (last added, first called) order. // Cleanup procedures will be called in LIFO (last added, first called) order.
// Each procedure will use a copy of the context at the time of registering.
cleanup :: proc(t: ^T, procedure: proc(rawptr), user_data: rawptr) { cleanup :: proc(t: ^T, procedure: proc(rawptr), user_data: rawptr) {
append(&t.cleanups, Internal_Cleanup{procedure, user_data}) append(&t.cleanups, Internal_Cleanup{procedure, user_data, context})
} }
expect :: proc(t: ^T, ok: bool, msg: string = "", loc := #caller_location) -> bool { expect :: proc(t: ^T, ok: bool, msg: string = "", loc := #caller_location) -> bool {
if !ok { if !ok {
error(t, msg, loc=loc) pkg_log.error(msg, location=loc)
} }
return ok return ok
} }
expectf :: proc(t: ^T, ok: bool, format: string, args: ..any, loc := #caller_location) -> bool { expectf :: proc(t: ^T, ok: bool, format: string, args: ..any, loc := #caller_location) -> bool {
if !ok { if !ok {
errorf(t, format, ..args, loc=loc) pkg_log.errorf(format, ..args, location=loc)
} }
return ok return ok
} }
@@ -102,12 +116,15 @@ expectf :: proc(t: ^T, ok: bool, format: string, args: ..any, loc := #caller_loc
expect_value :: proc(t: ^T, value, expected: $T, loc := #caller_location) -> bool where intrinsics.type_is_comparable(T) { expect_value :: proc(t: ^T, value, expected: $T, loc := #caller_location) -> bool where intrinsics.type_is_comparable(T) {
ok := value == expected || reflect.is_nil(value) && reflect.is_nil(expected) ok := value == expected || reflect.is_nil(value) && reflect.is_nil(expected)
if !ok { if !ok {
errorf(t, "expected %v, got %v", expected, value, loc=loc) pkg_log.errorf("expected %v, got %v", expected, value, location=loc)
} }
return ok return ok
} }
set_fail_timeout :: proc(t: ^T, duration: time.Duration, loc := #caller_location) { set_fail_timeout :: proc(t: ^T, duration: time.Duration, loc := #caller_location) {
_fail_timeout(t, duration, loc) chan.send(t.channel, Event_Set_Fail_Timeout {
at_time = time.time_add(time.now(), duration),
location = loc,
})
} }
-2
View File
@@ -162,8 +162,6 @@ parse_qt_linguist_file :: proc(filename: string, options := DEFAULT_PARSE_OPTION
context.allocator = allocator context.allocator = allocator
data, data_ok := os.read_entire_file(filename) data, data_ok := os.read_entire_file(filename)
defer delete(data)
if !data_ok { return {}, .File_Error } if !data_ok { return {}, .File_Error }
return parse_qt_linguist_from_bytes(data, options, pluralizer, allocator) return parse_qt_linguist_from_bytes(data, options, pluralizer, allocator)
+118 -15
View File
@@ -44,6 +44,29 @@ Pool :: struct {
tasks_done: [dynamic]Task, tasks_done: [dynamic]Task,
} }
Pool_Thread_Data :: struct {
pool: ^Pool,
task: Task,
}
@(private="file")
pool_thread_runner :: proc(t: ^Thread) {
data := cast(^Pool_Thread_Data)t.data
pool := data.pool
for intrinsics.atomic_load(&pool.is_running) {
sync.wait(&pool.sem_available)
if task, ok := pool_pop_waiting(pool); ok {
data.task = task
pool_do_work(pool, task)
data.task = {}
}
}
sync.post(&pool.sem_available, 1)
}
// Once initialized, the pool's memory address is not allowed to change until // Once initialized, the pool's memory address is not allowed to change until
// it is destroyed. // it is destroyed.
// //
@@ -58,21 +81,11 @@ pool_init :: proc(pool: ^Pool, allocator: mem.Allocator, thread_count: int) {
pool.is_running = true pool.is_running = true
for _, i in pool.threads { for _, i in pool.threads {
t := create(proc(t: ^Thread) { t := create(pool_thread_runner)
pool := (^Pool)(t.data) data := new(Pool_Thread_Data)
data.pool = pool
for intrinsics.atomic_load(&pool.is_running) {
sync.wait(&pool.sem_available)
if task, ok := pool_pop_waiting(pool); ok {
pool_do_work(pool, task)
}
}
sync.post(&pool.sem_available, 1)
})
t.user_index = i t.user_index = i
t.data = pool t.data = data
pool.threads[i] = t pool.threads[i] = t
} }
} }
@@ -82,6 +95,8 @@ pool_destroy :: proc(pool: ^Pool) {
delete(pool.tasks_done) delete(pool.tasks_done)
for &t in pool.threads { for &t in pool.threads {
data := cast(^Pool_Thread_Data)t.data
free(data, pool.allocator)
destroy(t) destroy(t)
} }
@@ -103,7 +118,7 @@ pool_join :: proc(pool: ^Pool) {
yield() yield()
started_count: int started_count: int
for started_count < len(pool.threads) { for started_count < len(pool.threads) {
started_count = 0 started_count = 0
for t in pool.threads { for t in pool.threads {
@@ -138,6 +153,94 @@ pool_add_task :: proc(pool: ^Pool, allocator: mem.Allocator, procedure: Task_Pro
sync.post(&pool.sem_available, 1) sync.post(&pool.sem_available, 1)
} }
// Forcibly stop a running task by its user index.
//
// This will terminate the underlying thread. Ideally, you should use some
// means of communication to stop a task, as thread termination may leave
// resources unclaimed.
//
// The thread will be restarted to accept new tasks.
//
// Returns true if the task was found and terminated.
pool_stop_task :: proc(pool: ^Pool, user_index: int, exit_code: int = 1) -> bool {
sync.guard(&pool.mutex)
for t, i in pool.threads {
data := cast(^Pool_Thread_Data)t.data
if data.task.user_index == user_index && data.task.procedure != nil {
terminate(t, exit_code)
append(&pool.tasks_done, data.task)
intrinsics.atomic_add(&pool.num_done, 1)
intrinsics.atomic_sub(&pool.num_outstanding, 1)
intrinsics.atomic_sub(&pool.num_in_processing, 1)
destroy(t)
replacement := create(pool_thread_runner)
replacement.user_index = t.user_index
replacement.data = data
data.task = {}
pool.threads[i] = replacement
start(replacement)
return true
}
}
return false
}
// Forcibly stop all running tasks.
//
// The same notes from `pool_stop_task` apply here.
pool_stop_all_tasks :: proc(pool: ^Pool, exit_code: int = 1) {
sync.guard(&pool.mutex)
for t, i in pool.threads {
data := cast(^Pool_Thread_Data)t.data
if data.task.procedure != nil {
terminate(t, exit_code)
append(&pool.tasks_done, data.task)
intrinsics.atomic_add(&pool.num_done, 1)
intrinsics.atomic_sub(&pool.num_outstanding, 1)
intrinsics.atomic_sub(&pool.num_in_processing, 1)
destroy(t)
replacement := create(pool_thread_runner)
replacement.user_index = t.user_index
replacement.data = data
data.task = {}
pool.threads[i] = replacement
start(replacement)
}
}
}
// Force the pool to stop all of its threads and put it into a state where
// it will no longer run any more tasks.
//
// The pool must still be destroyed after this.
pool_shutdown :: proc(pool: ^Pool, exit_code: int = 1) {
intrinsics.atomic_store(&pool.is_running, false)
sync.guard(&pool.mutex)
for t in pool.threads {
terminate(t, exit_code)
data := cast(^Pool_Thread_Data)t.data
if data.task.procedure != nil {
append(&pool.tasks_done, data.task)
intrinsics.atomic_add(&pool.num_done, 1)
intrinsics.atomic_sub(&pool.num_outstanding, 1)
intrinsics.atomic_sub(&pool.num_in_processing, 1)
}
}
}
// Number of tasks waiting to be processed. Only informational, mostly for // Number of tasks waiting to be processed. Only informational, mostly for
// debugging. Don't rely on this value being consistent with other num_* // debugging. Don't rely on this value being consistent with other num_*
// values. // values.
-1
View File
@@ -893,7 +893,6 @@ struct BuildContext {
u32 cmd_doc_flags; u32 cmd_doc_flags;
Array<String> extra_packages; Array<String> extra_packages;
StringSet test_names;
bool test_all_packages; bool test_all_packages;
gbAffinity affinity; gbAffinity affinity;
-29
View File
@@ -5852,35 +5852,6 @@ gb_internal void remove_neighbouring_duplicate_entires_from_sorted_array(Array<E
gb_internal void check_test_procedures(Checker *c) { gb_internal void check_test_procedures(Checker *c) {
array_sort(c->info.testing_procedures, init_procedures_cmp); array_sort(c->info.testing_procedures, init_procedures_cmp);
remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.testing_procedures); remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.testing_procedures);
if (build_context.test_names.entries.count == 0) {
return;
}
AstPackage *pkg = c->info.init_package;
Scope *s = pkg->scope;
for (String const &name : build_context.test_names) {
Entity *e = scope_lookup(s, name);
if (e == nullptr) {
Token tok = {};
if (pkg->files.count != 0) {
tok = pkg->files[0]->tokens[0];
}
error(tok, "Unable to find the test '%.*s' in 'package %.*s' ", LIT(name), LIT(pkg->name));
}
}
for (isize i = 0; i < c->info.testing_procedures.count; /**/) {
Entity *e = c->info.testing_procedures[i];
String name = e->token.string;
if (!string_set_exists(&build_context.test_names, name)) {
array_ordered_remove(&c->info.testing_procedures, i);
} else {
i += 1;
}
}
} }
-24
View File
@@ -276,8 +276,6 @@ enum BuildFlagKind {
BuildFlag_RelocMode, BuildFlag_RelocMode,
BuildFlag_DisableRedZone, BuildFlag_DisableRedZone,
BuildFlag_TestName,
BuildFlag_DisallowDo, BuildFlag_DisallowDo,
BuildFlag_DefaultToNilAllocator, BuildFlag_DefaultToNilAllocator,
BuildFlag_DefaultToPanicAllocator, BuildFlag_DefaultToPanicAllocator,
@@ -471,8 +469,6 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_RelocMode, str_lit("reloc-mode"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_RelocMode, str_lit("reloc-mode"), BuildFlagParam_String, Command__does_build);
add_flag(&build_flags, BuildFlag_DisableRedZone, str_lit("disable-red-zone"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_DisableRedZone, str_lit("disable-red-zone"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_TestName, str_lit("test-name"), BuildFlagParam_String, Command_test);
add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_DefaultToPanicAllocator, str_lit("default-to-panic-allocator"),BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_DefaultToPanicAllocator, str_lit("default-to-panic-allocator"),BuildFlagParam_None, Command__does_check);
@@ -1119,21 +1115,6 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_DisableRedZone: case BuildFlag_DisableRedZone:
build_context.disable_red_zone = true; build_context.disable_red_zone = true;
break; break;
case BuildFlag_TestName: {
GB_ASSERT(value.kind == ExactValue_String);
{
String name = value.value_string;
if (!string_is_valid_identifier(name)) {
gb_printf_err("Test name '%.*s' must be a valid identifier\n", LIT(name));
bad_flags = true;
break;
}
string_set_add(&build_context.test_names, name);
// NOTE(bill): Allow for multiple -test-name
continue;
}
}
case BuildFlag_DisallowDo: case BuildFlag_DisallowDo:
build_context.disallow_do = true; build_context.disallow_do = true;
break; break;
@@ -1962,10 +1943,6 @@ gb_internal void print_show_help(String const arg0, String const &command) {
} }
if (test_only) { if (test_only) {
print_usage_line(1, "-test-name:<string>");
print_usage_line(2, "Runs specific test only by name.");
print_usage_line(0, "");
print_usage_line(1, "-all-packages"); print_usage_line(1, "-all-packages");
print_usage_line(2, "Tests all packages imported into the given initial package."); print_usage_line(2, "Tests all packages imported into the given initial package.");
print_usage_line(0, ""); print_usage_line(0, "");
@@ -2489,7 +2466,6 @@ int main(int arg_count, char const **arg_ptr) {
TIME_SECTION("init args"); TIME_SECTION("init args");
map_init(&build_context.defined_values); map_init(&build_context.defined_values);
build_context.extra_packages.allocator = heap_allocator(); build_context.extra_packages.allocator = heap_allocator();
string_set_init(&build_context.test_names);
Array<String> args = setup_args(arg_count, arg_ptr); Array<String> args = setup_args(arg_count, arg_ptr);
+14
View File
@@ -0,0 +1,14 @@
ODIN=../../odin
COMMON=-no-bounds-check -vet -strict-style -define:ODIN_TEST_FANCY=false
all: crypto_bench \
hash_bench
crypto_bench:
$(ODIN) test crypto $(COMMON) -o:speed -out:bench_crypto
hash_bench:
$(ODIN) test hash $(COMMON) -o:speed -out:bench_hash
clean:
rm bench_*
+13
View File
@@ -0,0 +1,13 @@
@echo off
set COMMON=-no-bounds-check -vet -strict-style -define:ODIN_TEST_FANCY=false
set PATH_TO_ODIN==..\..\odin
echo ---
echo Running core:crypto benchmarks
echo ---
%PATH_TO_ODIN% test crypto %COMMON% -o:speed -out:bench_crypto.exe || exit /b
echo ---
echo Running core:hash benchmarks
echo ---
%PATH_TO_ODIN% test hash %COMMON% -o:speed -out:bench_hash.exe || exit /b
@@ -0,0 +1,356 @@
package benchmark_core_crypto
import "base:runtime"
import "core:encoding/hex"
import "core:fmt"
import "core:log"
import "core:strings"
import "core:testing"
import "core:time"
import "core:crypto/aes"
import "core:crypto/chacha20"
import "core:crypto/chacha20poly1305"
import "core:crypto/ed25519"
import "core:crypto/poly1305"
import "core:crypto/x25519"
// Cryptographic primitive benchmarks.
@(test)
benchmark_crypto :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
str: strings.Builder
strings.builder_init(&str, context.allocator)
defer {
log.info(strings.to_string(str))
strings.builder_destroy(&str)
}
{
name := "ChaCha20 64 bytes"
options := &time.Benchmark_Options {
rounds = 1_000,
bytes = 64,
setup = _setup_sized_buf,
bench = _benchmark_chacha20,
teardown = _teardown_sized_buf,
}
err := time.benchmark(options, context.allocator)
testing.expect(t, err == nil, name)
benchmark_print(&str, name, options)
name = "ChaCha20 1024 bytes"
options.bytes = 1024
err = time.benchmark(options, context.allocator)
testing.expect(t, err == nil, name)
benchmark_print(&str, name, options)
name = "ChaCha20 65536 bytes"
options.bytes = 65536
err = time.benchmark(options, context.allocator)
testing.expect(t, err == nil, name)
benchmark_print(&str, name, options)
}
{
name := "Poly1305 64 zero bytes"
options := &time.Benchmark_Options {
rounds = 1_000,
bytes = 64,
setup = _setup_sized_buf,
bench = _benchmark_poly1305,
teardown = _teardown_sized_buf,
}
err := time.benchmark(options, context.allocator)
testing.expect(t, err == nil, name)
benchmark_print(&str, name, options)
name = "Poly1305 1024 zero bytes"
options.bytes = 1024
err = time.benchmark(options, context.allocator)
testing.expect(t, err == nil, name)
benchmark_print(&str, name, options)
}
{
name := "chacha20poly1305 64 bytes"
options := &time.Benchmark_Options {
rounds = 1_000,
bytes = 64,
setup = _setup_sized_buf,
bench = _benchmark_chacha20poly1305,
teardown = _teardown_sized_buf,
}
err := time.benchmark(options, context.allocator)
testing.expect(t, err == nil, name)
benchmark_print(&str, name, options)
name = "chacha20poly1305 1024 bytes"
options.bytes = 1024
err = time.benchmark(options, context.allocator)
testing.expect(t, err == nil, name)
benchmark_print(&str, name, options)
name = "chacha20poly1305 65536 bytes"
options.bytes = 65536
err = time.benchmark(options, context.allocator)
testing.expect(t, err == nil, name)
benchmark_print(&str, name, options)
}
{
name := "AES256-GCM 64 bytes"
options := &time.Benchmark_Options {
rounds = 1_000,
bytes = 64,
setup = _setup_sized_buf,
bench = _benchmark_aes256_gcm,
teardown = _teardown_sized_buf,
}
key := [aes.KEY_SIZE_256]byte {
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
}
ctx: aes.Context_GCM
aes.init_gcm(&ctx, key[:])
context.user_ptr = &ctx
err := time.benchmark(options, context.allocator)
testing.expect(t, err == nil, name)
benchmark_print(&str, name, options)
name = "AES256-GCM 1024 bytes"
options.bytes = 1024
err = time.benchmark(options, context.allocator)
testing.expect(t, err == nil, name)
benchmark_print(&str, name, options)
name = "AES256-GCM 65536 bytes"
options.bytes = 65536
err = time.benchmark(options, context.allocator)
testing.expect(t, err == nil, name)
benchmark_print(&str, name, options)
}
{
iters :: 10000
priv_str := "cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"
priv_bytes, _ := hex.decode(transmute([]byte)(priv_str), context.temp_allocator)
priv_key: ed25519.Private_Key
start := time.now()
for i := 0; i < iters; i = i + 1 {
ok := ed25519.private_key_set_bytes(&priv_key, priv_bytes)
assert(ok, "private key should deserialize")
}
elapsed := time.since(start)
fmt.sbprintfln(&str,
"ed25519.private_key_set_bytes: ~%f us/op",
time.duration_microseconds(elapsed) / iters,
)
pub_bytes := priv_key._pub_key._b[:] // "I know what I am doing"
pub_key: ed25519.Public_Key
start = time.now()
for i := 0; i < iters; i = i + 1 {
ok := ed25519.public_key_set_bytes(&pub_key, pub_bytes[:])
assert(ok, "public key should deserialize")
}
elapsed = time.since(start)
fmt.sbprintfln(&str,
"ed25519.public_key_set_bytes: ~%f us/op",
time.duration_microseconds(elapsed) / iters,
)
msg := "Got a job for you, 621."
sig_bytes: [ed25519.SIGNATURE_SIZE]byte
msg_bytes := transmute([]byte)(msg)
start = time.now()
for i := 0; i < iters; i = i + 1 {
ed25519.sign(&priv_key, msg_bytes, sig_bytes[:])
}
elapsed = time.since(start)
fmt.sbprintfln(&str,
"ed25519.sign: ~%f us/op",
time.duration_microseconds(elapsed) / iters,
)
start = time.now()
for i := 0; i < iters; i = i + 1 {
ok := ed25519.verify(&pub_key, msg_bytes, sig_bytes[:])
assert(ok, "signature should validate")
}
elapsed = time.since(start)
fmt.sbprintfln(&str,
"ed25519.verify: ~%f us/op",
time.duration_microseconds(elapsed) / iters,
)
}
{
point_str := "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
scalar_str := "cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"
point, _ := hex.decode(transmute([]byte)(point_str), context.temp_allocator)
scalar, _ := hex.decode(transmute([]byte)(scalar_str), context.temp_allocator)
out: [x25519.POINT_SIZE]byte = ---
iters :: 10000
start := time.now()
for i := 0; i < iters; i = i + 1 {
x25519.scalarmult(out[:], scalar[:], point[:])
}
elapsed := time.since(start)
fmt.sbprintfln(&str,
"x25519.scalarmult: ~%f us/op",
time.duration_microseconds(elapsed) / iters,
)
}
}
@(private)
_setup_sized_buf :: proc(
options: ^time.Benchmark_Options,
allocator := context.allocator,
) -> (
err: time.Benchmark_Error,
) {
assert(options != nil)
options.input = make([]u8, options.bytes, allocator)
return nil if len(options.input) == options.bytes else .Allocation_Error
}
@(private)
_teardown_sized_buf :: proc(
options: ^time.Benchmark_Options,
allocator := context.allocator,
) -> (
err: time.Benchmark_Error,
) {
assert(options != nil)
delete(options.input)
return nil
}
@(private)
_benchmark_chacha20 :: proc(
options: ^time.Benchmark_Options,
allocator := context.allocator,
) -> (
err: time.Benchmark_Error,
) {
buf := options.input
key := [chacha20.KEY_SIZE]byte {
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
}
nonce := [chacha20.NONCE_SIZE]byte {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
}
ctx: chacha20.Context = ---
chacha20.init(&ctx, key[:], nonce[:])
for _ in 0 ..= options.rounds {
chacha20.xor_bytes(&ctx, buf, buf)
}
options.count = options.rounds
options.processed = options.rounds * options.bytes
return nil
}
@(private)
_benchmark_poly1305 :: proc(
options: ^time.Benchmark_Options,
allocator := context.allocator,
) -> (
err: time.Benchmark_Error,
) {
buf := options.input
key := [poly1305.KEY_SIZE]byte {
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
}
tag: [poly1305.TAG_SIZE]byte = ---
for _ in 0 ..= options.rounds {
poly1305.sum(tag[:], buf, key[:])
}
options.count = options.rounds
options.processed = options.rounds * options.bytes
//options.hash = u128(h)
return nil
}
@(private)
_benchmark_chacha20poly1305 :: proc(
options: ^time.Benchmark_Options,
allocator := context.allocator,
) -> (
err: time.Benchmark_Error,
) {
buf := options.input
key := [chacha20.KEY_SIZE]byte {
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
}
nonce := [chacha20.NONCE_SIZE]byte {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
}
tag: [chacha20poly1305.TAG_SIZE]byte = ---
for _ in 0 ..= options.rounds {
chacha20poly1305.encrypt(buf, tag[:], key[:], nonce[:], nil, buf)
}
options.count = options.rounds
options.processed = options.rounds * options.bytes
return nil
}
_benchmark_aes256_gcm :: proc(
options: ^time.Benchmark_Options,
allocator := context.allocator,
) -> (
err: time.Benchmark_Error,
) {
buf := options.input
nonce: [aes.GCM_NONCE_SIZE]byte
tag: [aes.GCM_TAG_SIZE]byte = ---
ctx := transmute(^aes.Context_GCM)context.user_ptr
for _ in 0 ..= options.rounds {
aes.seal_gcm(ctx, buf, tag[:], nonce[:], nil, buf)
}
options.count = options.rounds
options.processed = options.rounds * options.bytes
return nil
}
@(private)
benchmark_print :: proc(str: ^strings.Builder, name: string, options: ^time.Benchmark_Options, loc := #caller_location) {
fmt.sbprintfln(str, "[%v] %v rounds, %v bytes processed in %v ns\n\t\t%5.3f rounds/s, %5.3f MiB/s\n",
name,
options.rounds,
options.processed,
time.duration_nanoseconds(options.duration),
options.rounds_per_second,
options.megabytes_per_second,
)
}
+218
View File
@@ -0,0 +1,218 @@
package benchmark_core_hash
import "core:fmt"
import "core:hash/xxhash"
import "base:intrinsics"
import "core:strings"
import "core:testing"
import "core:time"
@(test)
benchmark_hash :: proc(t: ^testing.T) {
str: strings.Builder
strings.builder_init(&str, context.allocator)
defer {
fmt.println(strings.to_string(str))
strings.builder_destroy(&str)
}
{
name := "XXH32 100 zero bytes"
options := &time.Benchmark_Options{
rounds = 1_000,
bytes = 100,
setup = setup_xxhash,
bench = benchmark_xxh32,
teardown = teardown_xxhash,
}
err := time.benchmark(options, context.allocator)
testing.expectf(t, err == nil, "%s failed with err %v", name, err)
hash := u128(0x85f6413c)
testing.expectf(t, options.hash == hash, "%v hash expected to be %v, got %v", name, hash, options.hash)
benchmark_print(&str, name, options)
}
{
name := "XXH32 1 MiB zero bytes"
options := &time.Benchmark_Options{
rounds = 1_000,
bytes = 1_048_576,
setup = setup_xxhash,
bench = benchmark_xxh32,
teardown = teardown_xxhash,
}
err := time.benchmark(options, context.allocator)
testing.expectf(t, err == nil, "%s failed with err %v", name, err)
hash := u128(0x9430f97f)
testing.expectf(t, options.hash == hash, "%v hash expected to be %v, got %v", name, hash, options.hash)
benchmark_print(&str, name, options)
}
{
name := "XXH64 100 zero bytes"
options := &time.Benchmark_Options{
rounds = 1_000,
bytes = 100,
setup = setup_xxhash,
bench = benchmark_xxh64,
teardown = teardown_xxhash,
}
err := time.benchmark(options, context.allocator)
testing.expectf(t, err == nil, "%s failed with err %v", name, err)
hash := u128(0x17bb1103c92c502f)
testing.expectf(t, options.hash == hash, "%v hash expected to be %v, got %v", name, hash, options.hash)
benchmark_print(&str, name, options)
}
{
name := "XXH64 1 MiB zero bytes"
options := &time.Benchmark_Options{
rounds = 1_000,
bytes = 1_048_576,
setup = setup_xxhash,
bench = benchmark_xxh64,
teardown = teardown_xxhash,
}
err := time.benchmark(options, context.allocator)
testing.expectf(t, err == nil, "%s failed with err %v", name, err)
hash := u128(0x87d2a1b6e1163ef1)
testing.expectf(t, options.hash == hash, "%v hash expected to be %v, got %v", name, hash, options.hash)
benchmark_print(&str, name, options)
}
{
name := "XXH3_64 100 zero bytes"
options := &time.Benchmark_Options{
rounds = 1_000,
bytes = 100,
setup = setup_xxhash,
bench = benchmark_xxh3_64,
teardown = teardown_xxhash,
}
err := time.benchmark(options, context.allocator)
testing.expectf(t, err == nil, "%s failed with err %v", name, err)
hash := u128(0x801fedc74ccd608c)
testing.expectf(t, options.hash == hash, "%v hash expected to be %v, got %v", name, hash, options.hash)
benchmark_print(&str, name, options)
}
{
name := "XXH3_64 1 MiB zero bytes"
options := &time.Benchmark_Options{
rounds = 1_000,
bytes = 1_048_576,
setup = setup_xxhash,
bench = benchmark_xxh3_64,
teardown = teardown_xxhash,
}
err := time.benchmark(options, context.allocator)
testing.expectf(t, err == nil, "%s failed with err %v", name, err)
hash := u128(0x918780b90550bf34)
testing.expectf(t, options.hash == hash, "%v hash expected to be %v, got %v", name, hash, options.hash)
benchmark_print(&str, name, options)
}
{
name := "XXH3_128 100 zero bytes"
options := &time.Benchmark_Options{
rounds = 1_000,
bytes = 100,
setup = setup_xxhash,
bench = benchmark_xxh3_128,
teardown = teardown_xxhash,
}
err := time.benchmark(options, context.allocator)
testing.expectf(t, err == nil, "%s failed with err %v", name, err)
hash := u128(0x6ba30a4e9dffe1ff801fedc74ccd608c)
testing.expectf(t, options.hash == hash, "%v hash expected to be %v, got %v", name, hash, options.hash)
benchmark_print(&str, name, options)
}
{
name := "XXH3_128 1 MiB zero bytes"
options := &time.Benchmark_Options{
rounds = 1_000,
bytes = 1_048_576,
setup = setup_xxhash,
bench = benchmark_xxh3_128,
teardown = teardown_xxhash,
}
err := time.benchmark(options, context.allocator)
testing.expectf(t, err == nil, "%s failed with err %v", name, err)
hash := u128(0xb6ef17a3448492b6918780b90550bf34)
testing.expectf(t, options.hash == hash, "%v hash expected to be %v, got %v", name, hash, options.hash)
benchmark_print(&str, name, options)
}
}
// Benchmarks
setup_xxhash :: proc(options: ^time.Benchmark_Options, allocator := context.allocator) -> (err: time.Benchmark_Error) {
assert(options != nil)
options.input = make([]u8, options.bytes, allocator)
return nil if len(options.input) == options.bytes else .Allocation_Error
}
teardown_xxhash :: proc(options: ^time.Benchmark_Options, allocator := context.allocator) -> (err: time.Benchmark_Error) {
assert(options != nil)
delete(options.input)
return nil
}
benchmark_xxh32 :: proc(options: ^time.Benchmark_Options, allocator := context.allocator) -> (err: time.Benchmark_Error) {
buf := options.input
h: u32
for _ in 0..=options.rounds {
h = xxhash.XXH32(buf)
}
options.count = options.rounds
options.processed = options.rounds * options.bytes
options.hash = u128(h)
return nil
}
benchmark_xxh64 :: proc(options: ^time.Benchmark_Options, allocator := context.allocator) -> (err: time.Benchmark_Error) {
buf := options.input
h: u64
for _ in 0..=options.rounds {
h = xxhash.XXH64(buf)
}
options.count = options.rounds
options.processed = options.rounds * options.bytes
options.hash = u128(h)
return nil
}
benchmark_xxh3_64 :: proc(options: ^time.Benchmark_Options, allocator := context.allocator) -> (err: time.Benchmark_Error) {
buf := options.input
h: u64
for _ in 0..=options.rounds {
h = xxhash.XXH3_64(buf)
}
options.count = options.rounds
options.processed = options.rounds * options.bytes
options.hash = u128(h)
return nil
}
benchmark_xxh3_128 :: proc(options: ^time.Benchmark_Options, allocator := context.allocator) -> (err: time.Benchmark_Error) {
buf := options.input
h: u128
for _ in 0..=options.rounds {
h = xxhash.XXH3_128(buf)
}
options.count = options.rounds
options.processed = options.rounds * options.bytes
options.hash = h
return nil
}
benchmark_print :: proc(str: ^strings.Builder, name: string, options: ^time.Benchmark_Options, loc := #caller_location) {
fmt.sbprintfln(str, "[%v] %v rounds, %v bytes processed in %v ns\n\t\t%5.3f rounds/s, %5.3f MiB/s\n",
name,
options.rounds,
options.processed,
time.duration_nanoseconds(options.duration),
options.rounds_per_second,
options.megabytes_per_second,
)
}
-81
View File
@@ -1,81 +0,0 @@
// Boilerplate for tests
package common
import "core:testing"
import "core:fmt"
import "core:os"
import "core:strings"
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
errorf :: testing.errorf
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v:%s] FAIL %v\n", loc, loc.procedure, message)
return
}
}
errorf :: proc(t: ^testing.T, message: string, args: ..any, loc := #caller_location) {
TEST_fail += 1
fmt.printf("[%v:%s] Error %v\n", loc, loc.procedure, fmt.tprintf(message, ..args))
return
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
report :: proc(t: ^testing.T) {
if TEST_fail > 0 {
if TEST_fail > 1 {
fmt.printf("%v/%v tests successful, %v tests failed.\n", TEST_count - TEST_fail, TEST_count, TEST_fail)
} else {
fmt.printf("%v/%v tests successful, 1 test failed.\n", TEST_count - TEST_fail, TEST_count)
}
os.exit(1)
} else {
fmt.printf("%v/%v tests successful.\n", TEST_count, TEST_count)
}
}
// Returns absolute path to `sub_path` where `sub_path` is within the "tests/" sub-directory of the Odin project root
// and we're being run from the Odin project root or from a sub-directory of "tests/"
// e.g. get_data_path("assets/blah") will return "/Odin_root/tests/assets/blah" if run within "/Odin_root",
// "/Odin_root/tests" or "/Odin_root/tests/subdir" etc
get_data_path :: proc(t: ^testing.T, sub_path: string) -> (data_path: string) {
cwd := os.get_current_directory()
defer delete(cwd)
when ODIN_OS == .Windows {
norm, was_allocation := strings.replace_all(cwd, "\\", "/")
if !was_allocation {
norm = strings.clone(norm)
}
defer delete(norm)
} else {
norm := cwd
}
last_index := strings.last_index(norm, "/tests/")
if last_index == -1 {
len := len(norm)
if len >= 6 && norm[len-6:] == "/tests" {
data_path = fmt.tprintf("%s/%s", norm, sub_path)
} else {
data_path = fmt.tprintf("%s/tests/%s", norm, sub_path)
}
} else {
data_path = fmt.tprintf("%s/tests/%s", norm[:last_index], sub_path)
}
return data_path
}
+62 -57
View File
@@ -1,16 +1,15 @@
ODIN=../../odin ODIN=../../odin
PYTHON=$(shell which python3) PYTHON=$(shell which python3)
COMMON=-vet -strict-style COMMON=-no-bounds-check -vet -strict-style -define:ODIN_TEST_FANCY=false
COLLECTION=-collection:tests=..
all: all_bsd \ all: all_bsd \
net_test net_test
all_bsd: c_libc_test \ all_bsd: download_test_assets \
c_libc_test \
compress_test \ compress_test \
container_test \ container_test \
crypto_test \ crypto_test \
download_test_assets \
encoding_test \ encoding_test \
filepath_test \ filepath_test \
fmt_test \ fmt_test \
@@ -21,86 +20,92 @@ all_bsd: c_libc_test \
match_test \ match_test \
math_test \ math_test \
noise_test \ noise_test \
odin_test \
os_exit_test \ os_exit_test \
reflect_test \ reflect_test \
runtime_test \
slice_test \ slice_test \
strings_test \ strings_test \
thread_test \ thread_test \
runtime_test \ time_test
time_test \
fmt_test
download_test_assets: download_test_assets:
$(PYTHON) download_assets.py $(PYTHON) download_assets.py
image_test: c_libc_test:
$(ODIN) run image $(COMMON) -out:test_core_image $(ODIN) test c/libc $(COMMON) -out:test_core_libc
compress_test: compress_test:
$(ODIN) run compress $(COMMON) -out:test_core_compress $(ODIN) test compress $(COMMON) -out:test_core_compress
container_test: container_test:
$(ODIN) run container $(COMMON) $(COLLECTION) -out:test_core_container $(ODIN) test container $(COMMON) -out:test_core_container
strings_test:
$(ODIN) run strings $(COMMON) -out:test_core_strings
hash_test:
$(ODIN) run hash $(COMMON) -o:speed -no-bounds-check -out:test_hash
crypto_test: crypto_test:
$(ODIN) run crypto $(COMMON) $(COLLECTION) -o:speed -no-bounds-check -out:test_crypto $(ODIN) test crypto $(COMMON) -o:speed -out:test_crypto
noise_test:
$(ODIN) run math/noise $(COMMON) -out:test_noise
encoding_test: encoding_test:
$(ODIN) run encoding/hxa $(COMMON) $(COLLECTION) -out:test_hxa $(ODIN) test encoding/base64 $(COMMON) -out:test_base64
$(ODIN) run encoding/json $(COMMON) -out:test_json $(ODIN) test encoding/cbor $(COMMON) -out:test_cbor
$(ODIN) run encoding/varint $(COMMON) -out:test_varint $(ODIN) test encoding/hex $(COMMON) -out:test_hex
$(ODIN) run encoding/xml $(COMMON) -out:test_xml $(ODIN) test encoding/hxa $(COMMON) -out:test_hxa
$(ODIN) run encoding/cbor $(COMMON) -out:test_cbor $(ODIN) test encoding/json $(COMMON) -out:test_json
$(ODIN) run encoding/hex $(COMMON) -out:test_hex $(ODIN) test encoding/varint $(COMMON) -out:test_varint
$(ODIN) run encoding/base64 $(COMMON) -out:test_base64 $(ODIN) test encoding/xml $(COMMON) -out:test_xml
math_test:
$(ODIN) run math $(COMMON) $(COLLECTION) -out:test_core_math
linalg_glsl_math_test:
$(ODIN) run math/linalg/glsl $(COMMON) $(COLLECTION) -out:test_linalg_glsl_math
filepath_test: filepath_test:
$(ODIN) run path/filepath $(COMMON) $(COLLECTION) -out:test_core_filepath $(ODIN) test path/filepath $(COMMON) -out:test_core_filepath
reflect_test: fmt_test:
$(ODIN) run reflect $(COMMON) $(COLLECTION) -out:test_core_reflect $(ODIN) test fmt $(COMMON) -out:test_core_fmt
slice_test: hash_test:
$(ODIN) run slice $(COMMON) -out:test_core_slice $(ODIN) test hash $(COMMON) -o:speed -out:test_hash
image_test:
$(ODIN) test image $(COMMON) -out:test_core_image
i18n_test:
$(ODIN) test text/i18n $(COMMON) -out:test_core_i18n
match_test:
$(ODIN) test text/match $(COMMON) -out:test_core_match
math_test:
$(ODIN) test math $(COMMON) -out:test_core_math
linalg_glsl_math_test:
$(ODIN) test math/linalg/glsl $(COMMON) -out:test_linalg_glsl_math
noise_test:
$(ODIN) test math/noise $(COMMON) -out:test_noise
net_test:
$(ODIN) test net $(COMMON) -out:test_core_net
os_exit_test: os_exit_test:
$(ODIN) run os/test_core_os_exit.odin -file -out:test_core_os_exit && exit 1 || exit 0 $(ODIN) run os/test_core_os_exit.odin -file -out:test_core_os_exit && exit 1 || exit 0
i18n_test: odin_test:
$(ODIN) run text/i18n $(COMMON) -out:test_core_i18n $(ODIN) test odin $(COMMON) -out:test_core_odin
match_test: reflect_test:
$(ODIN) run text/match $(COMMON) -out:test_core_match $(ODIN) test reflect $(COMMON) -out:test_core_reflect
c_libc_test:
$(ODIN) run c/libc $(COMMON) -out:test_core_libc
net_test:
$(ODIN) run net $(COMMON) -out:test_core_net
fmt_test:
$(ODIN) run fmt $(COMMON) -out:test_core_fmt
thread_test:
$(ODIN) run thread $(COMMON) -out:test_core_thread
runtime_test: runtime_test:
$(ODIN) run runtime $(COMMON) -out:test_core_runtime $(ODIN) test runtime $(COMMON) -out:test_core_runtime
slice_test:
$(ODIN) test slice $(COMMON) -out:test_core_slice
strings_test:
$(ODIN) test strings $(COMMON) -out:test_core_strings
thread_test:
$(ODIN) test thread $(COMMON) -out:test_core_thread
time_test: time_test:
$(ODIN) run time $(COMMON) -out:test_core_time $(ODIN) test time $(COMMON) -out:test_core_time
clean:
rm test_*
+76 -67
View File
@@ -1,110 +1,119 @@
@echo off @echo off
set COMMON=-no-bounds-check -vet -strict-style set COMMON=-no-bounds-check -vet -strict-style -define:ODIN_TEST_FANCY=false
set COLLECTION=-collection:tests=..
set PATH_TO_ODIN==..\..\odin set PATH_TO_ODIN==..\..\odin
python3 download_assets.py python3 download_assets.py
echo ---
echo Running core:c/libc tests
echo ---
%PATH_TO_ODIN% test c\libc %COMMON% -out:test_libc.exe || exit /b
echo --- echo ---
echo Running core:compress tests echo Running core:compress tests
echo --- echo ---
%PATH_TO_ODIN% run compress %COMMON% -out:test_core_compress.exe || exit /b %PATH_TO_ODIN% test compress %COMMON% -out:test_core_compress.exe || exit /b
echo --- echo ---
echo Running core:container tests echo Running core:container tests
echo --- echo ---
%PATH_TO_ODIN% run container %COMMON% %COLLECTION% -out:test_core_container.exe || exit /b %PATH_TO_ODIN% test container %COMMON% -out:test_core_container.exe || exit /b
echo --- echo ---
echo Running core:crypto tests echo Running core:crypto tests
echo --- echo ---
%PATH_TO_ODIN% run crypto %COMMON% %COLLECTION% -out:test_crypto.exe || exit /b %PATH_TO_ODIN% test crypto %COMMON% -o:speed -out:test_crypto.exe || exit /b
echo --- echo ---
echo Running core:encoding tests echo Running core:encoding tests
echo --- echo ---
rem %PATH_TO_ODIN% run encoding/hxa %COMMON% %COLLECTION% -out:test_hxa.exe || exit /b %PATH_TO_ODIN% test encoding/base64 %COMMON% -out:test_base64.exe || exit /b
%PATH_TO_ODIN% run encoding/json %COMMON% -out:test_json.exe || exit /b %PATH_TO_ODIN% test encoding/cbor %COMMON% -out:test_cbor.exe || exit /b
%PATH_TO_ODIN% run encoding/varint %COMMON% -out:test_varint.exe || exit /b %PATH_TO_ODIN% test encoding/hex %COMMON% -out:test_hex.exe || exit /b
%PATH_TO_ODIN% run encoding/xml %COMMON% -out:test_xml.exe || exit /b %PATH_TO_ODIN% test encoding/hxa %COMMON% -out:test_hxa.exe || exit /b
%PATH_TO_ODIN% test encoding/cbor %COMMON% -out:test_cbor.exe || exit /b %PATH_TO_ODIN% test encoding/json %COMMON% -out:test_json.exe || exit /b
%PATH_TO_ODIN% run encoding/hex %COMMON% -out:test_hex.exe || exit /b %PATH_TO_ODIN% test encoding/varint %COMMON% -out:test_varint.exe || exit /b
%PATH_TO_ODIN% run encoding/base64 %COMMON% -out:test_base64.exe || exit /b %PATH_TO_ODIN% test encoding/xml %COMMON% -out:test_xml.exe || exit /b
echo ---
echo Running core:fmt tests
echo ---
%PATH_TO_ODIN% run fmt %COMMON% %COLLECTION% -out:test_core_fmt.exe || exit /b
echo ---
echo Running core:hash tests
echo ---
%PATH_TO_ODIN% run hash %COMMON% -o:size -out:test_core_hash.exe || exit /b
echo ---
echo Running core:image tests
echo ---
%PATH_TO_ODIN% run image %COMMON% -out:test_core_image.exe || exit /b
echo ---
echo Running core:math tests
echo ---
%PATH_TO_ODIN% run math %COMMON% %COLLECTION% -out:test_core_math.exe || exit /b
echo ---
echo Running core:math/linalg/glsl tests
echo ---
%PATH_TO_ODIN% run math/linalg/glsl %COMMON% %COLLECTION% -out:test_linalg_glsl.exe || exit /b
echo ---
echo Running core:math/noise tests
echo ---
%PATH_TO_ODIN% run math/noise %COMMON% -out:test_noise.exe || exit /b
echo ---
echo Running core:net
echo ---
%PATH_TO_ODIN% run net %COMMON% -out:test_core_net.exe || exit /b
echo ---
echo Running core:odin tests
echo ---
%PATH_TO_ODIN% run odin %COMMON% -o:size -out:test_core_odin.exe || exit /b
echo --- echo ---
echo Running core:path/filepath tests echo Running core:path/filepath tests
echo --- echo ---
%PATH_TO_ODIN% run path/filepath %COMMON% %COLLECTION% -out:test_core_filepath.exe || exit /b %PATH_TO_ODIN% test path/filepath %COMMON% -out:test_core_filepath.exe || exit /b
echo --- echo ---
echo Running core:reflect tests echo Running core:fmt tests
echo --- echo ---
%PATH_TO_ODIN% run reflect %COMMON% %COLLECTION% -out:test_core_reflect.exe || exit /b %PATH_TO_ODIN% test fmt %COMMON% -out:test_core_fmt.exe || exit /b
echo --- echo ---
echo Running core:runtime tests echo Running core:hash tests
echo --- echo ---
%PATH_TO_ODIN% run runtime %COMMON% %COLLECTION% -out:test_core_runtime.exe || exit /b %PATH_TO_ODIN% test hash %COMMON% -o:speed -out:test_core_hash.exe || exit /b
echo --- echo ---
echo Running core:slice tests echo Running core:image tests
echo --- echo ---
%PATH_TO_ODIN% run slice %COMMON% -out:test_core_slice.exe || exit /b %PATH_TO_ODIN% test image %COMMON% -out:test_core_image.exe || exit /b
echo ---
echo Running core:strings tests
echo ---
%PATH_TO_ODIN% run strings %COMMON% -out:test_core_strings.exe || exit /b
echo --- echo ---
echo Running core:text/i18n tests echo Running core:text/i18n tests
echo --- echo ---
%PATH_TO_ODIN% run text\i18n %COMMON% -out:test_core_i18n.exe || exit /b %PATH_TO_ODIN% test text\i18n %COMMON% -out:test_core_i18n.exe || exit /b
echo ---
echo Running text:match tests
echo ---
%PATH_TO_ODIN% test text/match %COMMON% -out:test_core_match.exe || exit /b
echo ---
echo Running core:math tests
echo ---
%PATH_TO_ODIN% test math %COMMON% -out:test_core_math.exe || exit /b
echo ---
echo Running core:math/linalg/glsl tests
echo ---
%PATH_TO_ODIN% test math/linalg/glsl %COMMON% -out:test_linalg_glsl.exe || exit /b
echo ---
echo Running core:math/noise tests
echo ---
%PATH_TO_ODIN% test math/noise %COMMON% -out:test_noise.exe || exit /b
echo ---
echo Running core:net
echo ---
%PATH_TO_ODIN% test net %COMMON% -out:test_core_net.exe || exit /b
echo ---
echo Running core:odin tests
echo ---
%PATH_TO_ODIN% test odin %COMMON% -o:size -out:test_core_odin.exe || exit /b
echo ---
echo Running core:reflect tests
echo ---
%PATH_TO_ODIN% test reflect %COMMON% -out:test_core_reflect.exe || exit /b
echo ---
echo Running core:runtime tests
echo ---
%PATH_TO_ODIN% test runtime %COMMON% -out:test_core_runtime.exe || exit /b
echo ---
echo Running core:slice tests
echo ---
%PATH_TO_ODIN% test slice %COMMON% -out:test_core_slice.exe || exit /b
echo ---
echo Running core:strings tests
echo ---
%PATH_TO_ODIN% test strings %COMMON% -out:test_core_strings.exe || exit /b
echo --- echo ---
echo Running core:thread tests echo Running core:thread tests
echo --- echo ---
%PATH_TO_ODIN% run thread %COMMON% %COLLECTION% -out:test_core_thread.exe || exit /b %PATH_TO_ODIN% test thread %COMMON% -out:test_core_thread.exe || exit /b
echo --- echo ---
echo Running core:time tests echo Running core:time tests
echo --- echo ---
%PATH_TO_ODIN% run time %COMMON% %COLLECTION% -out:test_core_time.exe || exit /b %PATH_TO_ODIN% test time %COMMON% -out:test_core_time.exe || exit /b
-36
View File
@@ -1,36 +0,0 @@
package test_core_libc
import "core:fmt"
import "core:os"
import "core:testing"
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
main :: proc() {
t := testing.T{}
test_libc_complex(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
@@ -1,8 +1,8 @@
package test_core_libc package test_core_libc
import "core:testing" import "core:testing"
import "core:fmt"
import "core:c/libc" import "core:c/libc"
import "core:log"
reldiff :: proc(lhs, rhs: $T) -> f64 { reldiff :: proc(lhs, rhs: $T) -> f64 {
if lhs == rhs { if lhs == rhs {
@@ -14,7 +14,7 @@ reldiff :: proc(lhs, rhs: $T) -> f64 {
return out return out
} }
isclose :: proc(lhs, rhs: $T, rtol:f64 = 1e-12, atol:f64 = 1e-12) -> bool { isclose :: proc(t: ^testing.T, lhs, rhs: $T, rtol:f64 = 1e-12, atol:f64 = 1e-12) -> bool {
adiff := f64(abs(lhs - rhs)) adiff := f64(abs(lhs - rhs))
if adiff < atol { if adiff < atol {
return true return true
@@ -23,7 +23,7 @@ isclose :: proc(lhs, rhs: $T, rtol:f64 = 1e-12, atol:f64 = 1e-12) -> bool {
if rdiff < rtol { if rdiff < rtol {
return true return true
} }
fmt.printf("not close -- lhs:%v rhs:%v -- adiff:%e rdiff:%e\n",lhs, rhs, adiff, rdiff) log.infof("not close -- lhs:%v rhs:%v -- adiff:%e rdiff:%e\n",lhs, rhs, adiff, rdiff)
return false return false
} }
@@ -44,7 +44,6 @@ test_libc_complex :: proc(t: ^testing.T) {
test_libc_pow_binding(t, libc.complex_float, f32, libc_powf, 1e-12, 1e-5) test_libc_pow_binding(t, libc.complex_float, f32, libc_powf, 1e-12, 1e-5)
} }
@test
test_libc_pow_binding :: proc(t: ^testing.T, $LIBC_COMPLEX:typeid, $F:typeid, pow: proc(LIBC_COMPLEX, LIBC_COMPLEX) -> LIBC_COMPLEX, test_libc_pow_binding :: proc(t: ^testing.T, $LIBC_COMPLEX:typeid, $F:typeid, pow: proc(LIBC_COMPLEX, LIBC_COMPLEX) -> LIBC_COMPLEX,
rtol: f64, atol: f64) { rtol: f64, atol: f64) {
// Tests that c/libc/pow(f) functions have two arguments and that the function works as expected for simple inputs // Tests that c/libc/pow(f) functions have two arguments and that the function works as expected for simple inputs
@@ -56,8 +55,8 @@ test_libc_pow_binding :: proc(t: ^testing.T, $LIBC_COMPLEX:typeid, $F:typeid, po
for n in -4..=4 { for n in -4..=4 {
complex_power := LIBC_COMPLEX(complex(F(n), F(0.))) complex_power := LIBC_COMPLEX(complex(F(n), F(0.)))
result := pow(complex_base, complex_power) result := pow(complex_base, complex_power)
expect(t, isclose(expected_real, F(real(result)), rtol, atol), fmt.tprintf("ftype:%T, n:%v reldiff(%v, re(%v)) is greater than specified rtol:%e", F{}, n, expected_real, result, rtol)) testing.expectf(t, isclose(t, expected_real, F(real(result)), rtol, atol), "ftype:%T, n:%v reldiff(%v, re(%v)) is greater than specified rtol:%e", F{}, n, expected_real, result, rtol)
expect(t, isclose(expected_imag, F(imag(result)), rtol, atol), fmt.tprintf("ftype:%T, n:%v reldiff(%v, im(%v)) is greater than specified rtol:%e", F{}, n, expected_imag, result, rtol)) testing.expectf(t, isclose(t, expected_imag, F(imag(result)), rtol, atol), "ftype:%T, n:%v reldiff(%v, im(%v)) is greater than specified rtol:%e", F{}, n, expected_imag, result, rtol)
expected_real *= 2 expected_real *= 2
} }
} }
@@ -83,8 +82,8 @@ test_libc_pow_binding :: proc(t: ^testing.T, $LIBC_COMPLEX:typeid, $F:typeid, po
expected_real = 0. expected_real = 0.
expected_imag = -value expected_imag = -value
} }
expect(t, isclose(expected_real, F(real(result)), rtol, atol), fmt.tprintf("ftype:%T, n:%v reldiff(%v, re(%v)) is greater than specified rtol:%e", F{}, n, expected_real, result, rtol)) testing.expectf(t, isclose(t, expected_real, F(real(result)), rtol, atol), "ftype:%T, n:%v reldiff(%v, re(%v)) is greater than specified rtol:%e", F{}, n, expected_real, result, rtol)
expect(t, isclose(expected_imag, F(imag(result)), rtol, atol), fmt.tprintf("ftype:%T, n:%v reldiff(%v, im(%v)) is greater than specified rtol:%e", F{}, n, expected_imag, result, rtol)) testing.expectf(t, isclose(t, expected_imag, F(imag(result)), rtol, atol), "ftype:%T, n:%v reldiff(%v, im(%v)) is greater than specified rtol:%e", F{}, n, expected_imag, result, rtol)
value *= 2 value *= 2
} }
} }
+17 -86
View File
@@ -15,47 +15,7 @@ import "core:testing"
import "core:compress/zlib" import "core:compress/zlib"
import "core:compress/gzip" import "core:compress/gzip"
import "core:compress/shoco" import "core:compress/shoco"
import "core:bytes" import "core:bytes"
import "core:fmt"
import "core:mem"
import "core:os"
import "core:io"
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
main :: proc() {
w := io.to_writer(os.stream_from_handle(os.stdout))
t := testing.T{w=w}
zlib_test(&t)
gzip_test(&t)
shoco_test(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
@test @test
zlib_test :: proc(t: ^testing.T) { zlib_test :: proc(t: ^testing.T) {
@@ -80,26 +40,14 @@ zlib_test :: proc(t: ^testing.T) {
} }
buf: bytes.Buffer buf: bytes.Buffer
err := zlib.inflate(ODIN_DEMO, &buf)
track: mem.Tracking_Allocator testing.expect(t, err == nil, "ZLIB failed to decompress ODIN_DEMO")
mem.tracking_allocator_init(&track, context.allocator)
context.allocator = mem.tracking_allocator(&track)
err := zlib.inflate(ODIN_DEMO, &buf)
expect(t, err == nil, "ZLIB failed to decompress ODIN_DEMO")
s := bytes.buffer_to_string(&buf) s := bytes.buffer_to_string(&buf)
expect(t, s[68] == 240 && s[69] == 159 && s[70] == 152, "ZLIB result should've contained 😃 at position 68.") testing.expect(t, s[68] == 240 && s[69] == 159 && s[70] == 152, "ZLIB result should've contained 😃 at position 68.")
testing.expect(t, len(s) == 438, "ZLIB result has an unexpected length.")
expect(t, len(s) == 438, "ZLIB result has an unexpected length.")
bytes.buffer_destroy(&buf) bytes.buffer_destroy(&buf)
for _, v in track.allocation_map {
error := fmt.tprintf("ZLIB test leaked %v bytes", v.size)
expect(t, false, error)
}
} }
@test @test
@@ -117,24 +65,12 @@ gzip_test :: proc(t: ^testing.T) {
} }
buf: bytes.Buffer buf: bytes.Buffer
err := gzip.load(TEST, &buf)
track: mem.Tracking_Allocator testing.expect(t, err == nil, "GZIP failed to decompress TEST")
mem.tracking_allocator_init(&track, context.allocator) testing.expect(t, bytes.buffer_to_string(&buf) == "payload", "GZIP result wasn't 'payload'")
context.allocator = mem.tracking_allocator(&track)
err := gzip.load(TEST, &buf) // , 438);
expect(t, err == nil, "GZIP failed to decompress TEST")
s := bytes.buffer_to_string(&buf)
expect(t, s == "payload", "GZIP result wasn't 'payload'")
bytes.buffer_destroy(&buf) bytes.buffer_destroy(&buf)
for _, v in track.allocation_map {
error := fmt.tprintf("GZIP test leaked %v bytes", v.size)
expect(t, false, error)
}
} }
@test @test
@@ -168,31 +104,26 @@ shoco_test :: proc(t: ^testing.T) {
defer delete(buffer) defer delete(buffer)
size, err := shoco.decompress(v.compressed, buffer[:]) size, err := shoco.decompress(v.compressed, buffer[:])
msg := fmt.tprintf("Expected `decompress` to return `nil`, got %v", err) testing.expectf(t, err == nil, "Expected `decompress` to return `nil`, got %v", err)
expect(t, err == nil, msg)
msg = fmt.tprintf("Decompressed %v bytes into %v. Expected to decompress into %v bytes.", len(v.compressed), size, expected_raw) testing.expectf(t, size == expected_raw, "Decompressed %v bytes into %v. Expected to decompress into %v bytes", len(v.compressed), size, expected_raw)
expect(t, size == expected_raw, msg) testing.expect(t, string(buffer[:size]) == string(v.raw), "Decompressed contents don't match")
expect(t, string(buffer[:size]) == string(v.raw), "Decompressed contents don't match.")
size, err = shoco.compress(string(v.raw), buffer[:]) size, err = shoco.compress(string(v.raw), buffer[:])
expect(t, err == nil, "Expected `compress` to return `nil`.") testing.expect(t, err == nil, "Expected `compress` to return `nil`.")
msg = fmt.tprintf("Compressed %v bytes into %v. Expected to compress into %v bytes.", expected_raw, size, expected_compressed) testing.expectf(t, size == expected_compressed, "Compressed %v bytes into %v. Expected to compress into %v bytes", expected_raw, size, expected_compressed)
expect(t, size == expected_compressed, msg)
size, err = shoco.decompress(v.compressed, buffer[:expected_raw - 10]) size, err = shoco.decompress(v.compressed, buffer[:expected_raw - 10])
msg = fmt.tprintf("Decompressing into too small a buffer returned %v, expected `.Output_Too_Short`", err) testing.expectf(t, err == .Output_Too_Short, "Decompressing into too small a buffer returned %v, expected `.Output_Too_Short`", err)
expect(t, err == .Output_Too_Short, msg)
size, err = shoco.compress(string(v.raw), buffer[:expected_compressed - 10]) size, err = shoco.compress(string(v.raw), buffer[:expected_compressed - 10])
msg = fmt.tprintf("Compressing into too small a buffer returned %v, expected `.Output_Too_Short`", err) testing.expectf(t, err == .Output_Too_Short, "Compressing into too small a buffer returned %v, expected `.Output_Too_Short`", err)
expect(t, err == .Output_Too_Short, msg)
size, err = shoco.decompress(v.compressed[:v.short_pack], buffer[:]) size, err = shoco.decompress(v.compressed[:v.short_pack], buffer[:])
expect(t, err == .Stream_Too_Short, "Expected `decompress` to return `Stream_Too_Short` because there was no more data after selecting a pack.") testing.expectf(t, err == .Stream_Too_Short, "Insufficient data after pack returned %v, expected `.Stream_Too_Short`", err)
size, err = shoco.decompress(v.compressed[:v.short_sentinel], buffer[:]) size, err = shoco.decompress(v.compressed[:v.short_sentinel], buffer[:])
expect(t, err == .Stream_Too_Short, "Expected `decompress` to return `Stream_Too_Short` because there was no more data after non-ASCII sentinel.") testing.expectf(t, err == .Stream_Too_Short, "No more data after non-ASCII sentinel returned %v, expected `.Stream_Too_Short`", err)
} }
} }
+37 -36
View File
@@ -4,53 +4,54 @@ import "core:container/avl"
import "core:math/rand" import "core:math/rand"
import "core:slice" import "core:slice"
import "core:testing" import "core:testing"
import "core:fmt" import "core:log"
import tc "tests:common"
@(test) @(test)
test_avl :: proc(t: ^testing.T) { test_avl :: proc(t: ^testing.T) {
tc.log(t, fmt.tprintf("Testing avl, using random seed %v, add -define:RANDOM_SEED=%v to reuse it.", random_seed, random_seed)) log.infof("Testing avl using random seed %v.", t.seed)
// Initialization. // Initialization.
tree: avl.Tree(int) tree: avl.Tree(int)
avl.init(&tree, slice.cmp_proc(int)) avl.init(&tree, slice.cmp_proc(int))
tc.expect(t, avl.len(&tree) == 0, "empty: len should be 0") testing.expect(t, avl.len(&tree) == 0, "empty: len should be 0")
tc.expect(t, avl.first(&tree) == nil, "empty: first should be nil") testing.expect(t, avl.first(&tree) == nil, "empty: first should be nil")
tc.expect(t, avl.last(&tree) == nil, "empty: last should be nil") testing.expect(t, avl.last(&tree) == nil, "empty: last should be nil")
iter := avl.iterator(&tree, avl.Direction.Forward) iter := avl.iterator(&tree, avl.Direction.Forward)
tc.expect(t, avl.iterator_get(&iter) == nil, "empty/iterator: first node should be nil") testing.expect(t, avl.iterator_get(&iter) == nil, "empty/iterator: first node should be nil")
r: rand.Rand r: rand.Rand
rand.init(&r, random_seed) rand.init(&r, t.seed)
// Test insertion. // Test insertion.
NR_INSERTS :: 32 + 1 // Ensure at least 1 collision. NR_INSERTS :: 32 + 1 // Ensure at least 1 collision.
inserted_map := make(map[int]^avl.Node(int)) inserted_map := make(map[int]^avl.Node(int))
defer delete(inserted_map)
for i := 0; i < NR_INSERTS; i += 1 { for i := 0; i < NR_INSERTS; i += 1 {
v := int(rand.uint32(&r) & 0x1f) v := int(rand.uint32(&r) & 0x1f)
existing_node, in_map := inserted_map[v] existing_node, in_map := inserted_map[v]
n, ok, _ := avl.find_or_insert(&tree, v) n, ok, _ := avl.find_or_insert(&tree, v)
tc.expect(t, in_map != ok, "insert: ok should match inverse of map lookup") testing.expect(t, in_map != ok, "insert: ok should match inverse of map lookup")
if ok { if ok {
inserted_map[v] = n inserted_map[v] = n
} else { } else {
tc.expect(t, existing_node == n, "insert: expecting existing node") testing.expect(t, existing_node == n, "insert: expecting existing node")
} }
} }
nrEntries := len(inserted_map) nrEntries := len(inserted_map)
tc.expect(t, avl.len(&tree) == nrEntries, "insert: len after") testing.expect(t, avl.len(&tree) == nrEntries, "insert: len after")
validate_avl(t, &tree) validate_avl(t, &tree)
// Ensure that all entries can be found. // Ensure that all entries can be found.
for k, v in inserted_map { for k, v in inserted_map {
tc.expect(t, v == avl.find(&tree, k), "Find(): Node") testing.expect(t, v == avl.find(&tree, k), "Find(): Node")
tc.expect(t, k == v.value, "Find(): Node value") testing.expect(t, k == v.value, "Find(): Node value")
} }
// Test the forward/backward iterators. // Test the forward/backward iterators.
inserted_values: [dynamic]int inserted_values: [dynamic]int
defer delete(inserted_values)
for k in inserted_map { for k in inserted_map {
append(&inserted_values, k) append(&inserted_values, k)
} }
@@ -60,38 +61,38 @@ test_avl :: proc(t: ^testing.T) {
visited: int visited: int
for node in avl.iterator_next(&iter) { for node in avl.iterator_next(&iter) {
v, idx := node.value, visited v, idx := node.value, visited
tc.expect(t, inserted_values[idx] == v, "iterator/forward: value") testing.expect(t, inserted_values[idx] == v, "iterator/forward: value")
tc.expect(t, node == avl.iterator_get(&iter), "iterator/forward: get") testing.expect(t, node == avl.iterator_get(&iter), "iterator/forward: get")
visited += 1 visited += 1
} }
tc.expect(t, visited == nrEntries, "iterator/forward: visited") testing.expect(t, visited == nrEntries, "iterator/forward: visited")
slice.reverse(inserted_values[:]) slice.reverse(inserted_values[:])
iter = avl.iterator(&tree, avl.Direction.Backward) iter = avl.iterator(&tree, avl.Direction.Backward)
visited = 0 visited = 0
for node in avl.iterator_next(&iter) { for node in avl.iterator_next(&iter) {
v, idx := node.value, visited v, idx := node.value, visited
tc.expect(t, inserted_values[idx] == v, "iterator/backward: value") testing.expect(t, inserted_values[idx] == v, "iterator/backward: value")
visited += 1 visited += 1
} }
tc.expect(t, visited == nrEntries, "iterator/backward: visited") testing.expect(t, visited == nrEntries, "iterator/backward: visited")
// Test removal. // Test removal.
rand.shuffle(inserted_values[:], &r) rand.shuffle(inserted_values[:], &r)
for v, i in inserted_values { for v, i in inserted_values {
node := avl.find(&tree, v) node := avl.find(&tree, v)
tc.expect(t, node != nil, "remove: find (pre)") testing.expect(t, node != nil, "remove: find (pre)")
ok := avl.remove(&tree, v) ok := avl.remove(&tree, v)
tc.expect(t, ok, "remove: succeeds") testing.expect(t, ok, "remove: succeeds")
tc.expect(t, nrEntries - (i + 1) == avl.len(&tree), "remove: len (post)") testing.expect(t, nrEntries - (i + 1) == avl.len(&tree), "remove: len (post)")
validate_avl(t, &tree) validate_avl(t, &tree)
tc.expect(t, nil == avl.find(&tree, v), "remove: find (post") testing.expect(t, nil == avl.find(&tree, v), "remove: find (post")
} }
tc.expect(t, avl.len(&tree) == 0, "remove: len should be 0") testing.expect(t, avl.len(&tree) == 0, "remove: len should be 0")
tc.expect(t, avl.first(&tree) == nil, "remove: first should be nil") testing.expect(t, avl.first(&tree) == nil, "remove: first should be nil")
tc.expect(t, avl.last(&tree) == nil, "remove: last should be nil") testing.expect(t, avl.last(&tree) == nil, "remove: last should be nil")
// Refill the tree. // Refill the tree.
for v in inserted_values { for v in inserted_values {
@@ -104,25 +105,25 @@ test_avl :: proc(t: ^testing.T) {
v := node.value v := node.value
ok := avl.iterator_remove(&iter) ok := avl.iterator_remove(&iter)
tc.expect(t, ok, "iterator/remove: success") testing.expect(t, ok, "iterator/remove: success")
ok = avl.iterator_remove(&iter) ok = avl.iterator_remove(&iter)
tc.expect(t, !ok, "iterator/remove: redundant removes should fail") testing.expect(t, !ok, "iterator/remove: redundant removes should fail")
tc.expect(t, avl.find(&tree, v) == nil, "iterator/remove: node should be gone") testing.expect(t, avl.find(&tree, v) == nil, "iterator/remove: node should be gone")
tc.expect(t, avl.iterator_get(&iter) == nil, "iterator/remove: get should return nil") testing.expect(t, avl.iterator_get(&iter) == nil, "iterator/remove: get should return nil")
// Ensure that iterator_next still works. // Ensure that iterator_next still works.
node, ok = avl.iterator_next(&iter) node, ok = avl.iterator_next(&iter)
tc.expect(t, ok == (avl.len(&tree) > 0), "iterator/remove: next should return false") testing.expect(t, ok == (avl.len(&tree) > 0), "iterator/remove: next should return false")
tc.expect(t, node == avl.first(&tree), "iterator/remove: next should return first") testing.expect(t, node == avl.first(&tree), "iterator/remove: next should return first")
validate_avl(t, &tree) validate_avl(t, &tree)
} }
tc.expect(t, avl.len(&tree) == nrEntries - 1, "iterator/remove: len should drop by 1") testing.expect(t, avl.len(&tree) == nrEntries - 1, "iterator/remove: len should drop by 1")
avl.destroy(&tree) avl.destroy(&tree)
tc.expect(t, avl.len(&tree) == 0, "destroy: len should be 0") testing.expect(t, avl.len(&tree) == 0, "destroy: len should be 0")
} }
@(private) @(private)
@@ -141,10 +142,10 @@ tree_check_invariants :: proc(
} }
// Validate the parent pointer. // Validate the parent pointer.
tc.expect(t, parent == node._parent, "invalid parent pointer") testing.expect(t, parent == node._parent, "invalid parent pointer")
// Validate that the balance factor is -1, 0, 1. // Validate that the balance factor is -1, 0, 1.
tc.expect( testing.expect(
t, t,
node._balance == -1 || node._balance == 0 || node._balance == 1, node._balance == -1 || node._balance == 0 || node._balance == 1,
"invalid balance factor", "invalid balance factor",
@@ -155,7 +156,7 @@ tree_check_invariants :: proc(
r_height := tree_check_invariants(t, tree, node._right, node) r_height := tree_check_invariants(t, tree, node._right, node)
// Validate the AVL invariant and the balance factor. // Validate the AVL invariant and the balance factor.
tc.expect(t, int(node._balance) == r_height - l_height, "AVL balance factor invariant violated") testing.expect(t, int(node._balance) == r_height - l_height, "AVL balance factor invariant violated")
if l_height > r_height { if l_height > r_height {
return l_height + 1 return l_height + 1
} }
@@ -1,26 +0,0 @@
package test_core_container
import "core:fmt"
import "core:testing"
import tc "tests:common"
expect_equal :: proc(t: ^testing.T, the_slice, expected: []int, loc := #caller_location) {
_eq :: proc(a, b: []int) -> bool {
if len(a) != len(b) do return false
for a, i in a {
if b[i] != a do return false
}
return true
}
tc.expect(t, _eq(the_slice, expected), fmt.tprintf("Expected %v, got %v\n", the_slice, expected), loc)
}
main :: proc() {
t := testing.T{}
test_avl(&t)
test_rbtree(&t)
test_small_array(&t)
tc.report(&t)
}
+43 -47
View File
@@ -3,14 +3,10 @@ package test_core_container
import rb "core:container/rbtree" import rb "core:container/rbtree"
import "core:math/rand" import "core:math/rand"
import "core:testing" import "core:testing"
import "core:fmt"
import "base:intrinsics" import "base:intrinsics"
import "core:mem" import "core:mem"
import "core:slice" import "core:slice"
import tc "tests:common" import "core:log"
RANDOM_SEED :: #config(RANDOM_SEED, 0)
random_seed := u64(intrinsics.read_cycle_counter()) when RANDOM_SEED == 0 else u64(RANDOM_SEED)
test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) { test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) {
track: mem.Tracking_Allocator track: mem.Tracking_Allocator
@@ -19,17 +15,17 @@ test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) {
context.allocator = mem.tracking_allocator(&track) context.allocator = mem.tracking_allocator(&track)
r: rand.Rand r: rand.Rand
rand.init(&r, random_seed) rand.init(&r, t.seed)
tc.log(t, fmt.tprintf("Testing Red-Black Tree($Key=%v,$Value=%v), using random seed %v, add -define:RANDOM_SEED=%v to reuse it.", type_info_of(Key), type_info_of(Value), random_seed, random_seed)) log.infof("Testing Red-Black Tree($Key=%v,$Value=%v) using random seed %v.", type_info_of(Key), type_info_of(Value), t.seed)
tree: rb.Tree(Key, Value) tree: rb.Tree(Key, Value)
rb.init(&tree) rb.init(&tree)
tc.expect(t, rb.len(&tree) == 0, "empty: len should be 0") testing.expect(t, rb.len(&tree) == 0, "empty: len should be 0")
tc.expect(t, rb.first(&tree) == nil, "empty: first should be nil") testing.expect(t, rb.first(&tree) == nil, "empty: first should be nil")
tc.expect(t, rb.last(&tree) == nil, "empty: last should be nil") testing.expect(t, rb.last(&tree) == nil, "empty: last should be nil")
iter := rb.iterator(&tree, .Forward) iter := rb.iterator(&tree, .Forward)
tc.expect(t, rb.iterator_get(&iter) == nil, "empty/iterator: first node should be nil") testing.expect(t, rb.iterator_get(&iter) == nil, "empty/iterator: first node should be nil")
// Test insertion. // Test insertion.
NR_INSERTS :: 32 + 1 // Ensure at least 1 collision. NR_INSERTS :: 32 + 1 // Ensure at least 1 collision.
@@ -45,27 +41,27 @@ test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) {
existing_node, in_map := inserted_map[k] existing_node, in_map := inserted_map[k]
n, inserted, _ := rb.find_or_insert(&tree, k, v) n, inserted, _ := rb.find_or_insert(&tree, k, v)
tc.expect(t, in_map != inserted, "insert: inserted should match inverse of map lookup") testing.expect(t, in_map != inserted, "insert: inserted should match inverse of map lookup")
if inserted { if inserted {
inserted_map[k] = n inserted_map[k] = n
} else { } else {
tc.expect(t, existing_node == n, "insert: expecting existing node") testing.expect(t, existing_node == n, "insert: expecting existing node")
} }
} }
entry_count := len(inserted_map) entry_count := len(inserted_map)
tc.expect(t, rb.len(&tree) == entry_count, "insert: len after") testing.expect(t, rb.len(&tree) == entry_count, "insert: len after")
validate_rbtree(t, &tree) validate_rbtree(t, &tree)
first := rb.first(&tree) first := rb.first(&tree)
last := rb.last(&tree) last := rb.last(&tree)
tc.expect(t, first != nil && first.key == min_key, fmt.tprintf("insert: first should be present with key %v", min_key)) testing.expectf(t, first != nil && first.key == min_key, "insert: first should be present with key %v", min_key)
tc.expect(t, last != nil && last.key == max_key, fmt.tprintf("insert: last should be present with key %v", max_key)) testing.expectf(t, last != nil && last.key == max_key, "insert: last should be present with key %v", max_key)
// Ensure that all entries can be found. // Ensure that all entries can be found.
for k, v in inserted_map { for k, v in inserted_map {
tc.expect(t, v == rb.find(&tree, k), "Find(): Node") testing.expect(t, v == rb.find(&tree, k), "Find(): Node")
tc.expect(t, k == v.key, "Find(): Node key") testing.expect(t, k == v.key, "Find(): Node key")
} }
// Test the forward/backward iterators. // Test the forward/backward iterators.
@@ -79,21 +75,21 @@ test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) {
visited: int visited: int
for node in rb.iterator_next(&iter) { for node in rb.iterator_next(&iter) {
k, idx := node.key, visited k, idx := node.key, visited
tc.expect(t, inserted_keys[idx] == k, "iterator/forward: key") testing.expect(t, inserted_keys[idx] == k, "iterator/forward: key")
tc.expect(t, node == rb.iterator_get(&iter), "iterator/forward: get") testing.expect(t, node == rb.iterator_get(&iter), "iterator/forward: get")
visited += 1 visited += 1
} }
tc.expect(t, visited == entry_count, "iterator/forward: visited") testing.expect(t, visited == entry_count, "iterator/forward: visited")
slice.reverse(inserted_keys[:]) slice.reverse(inserted_keys[:])
iter = rb.iterator(&tree, rb.Direction.Backward) iter = rb.iterator(&tree, rb.Direction.Backward)
visited = 0 visited = 0
for node in rb.iterator_next(&iter) { for node in rb.iterator_next(&iter) {
k, idx := node.key, visited k, idx := node.key, visited
tc.expect(t, inserted_keys[idx] == k, "iterator/backward: key") testing.expect(t, inserted_keys[idx] == k, "iterator/backward: key")
visited += 1 visited += 1
} }
tc.expect(t, visited == entry_count, "iterator/backward: visited") testing.expect(t, visited == entry_count, "iterator/backward: visited")
// Test removal (and on_remove callback) // Test removal (and on_remove callback)
rand.shuffle(inserted_keys[:], &r) rand.shuffle(inserted_keys[:], &r)
@@ -104,19 +100,19 @@ test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) {
} }
for k, i in inserted_keys { for k, i in inserted_keys {
node := rb.find(&tree, k) node := rb.find(&tree, k)
tc.expect(t, node != nil, "remove: find (pre)") testing.expect(t, node != nil, "remove: find (pre)")
ok := rb.remove(&tree, k) ok := rb.remove(&tree, k)
tc.expect(t, ok, "remove: succeeds") testing.expect(t, ok, "remove: succeeds")
tc.expect(t, entry_count - (i + 1) == rb.len(&tree), "remove: len (post)") testing.expect(t, entry_count - (i + 1) == rb.len(&tree), "remove: len (post)")
validate_rbtree(t, &tree) validate_rbtree(t, &tree)
tc.expect(t, nil == rb.find(&tree, k), "remove: find (post") testing.expect(t, nil == rb.find(&tree, k), "remove: find (post")
} }
tc.expect(t, rb.len(&tree) == 0, "remove: len should be 0") testing.expect(t, rb.len(&tree) == 0, "remove: len should be 0")
tc.expect(t, callback_count == 0, fmt.tprintf("remove: on_remove should've been called %v times, it was %v", entry_count, callback_count)) testing.expectf(t, callback_count == 0, "remove: on_remove should've been called %v times, it was %v", entry_count, callback_count)
tc.expect(t, rb.first(&tree) == nil, "remove: first should be nil") testing.expect(t, rb.first(&tree) == nil, "remove: first should be nil")
tc.expect(t, rb.last(&tree) == nil, "remove: last should be nil") testing.expect(t, rb.last(&tree) == nil, "remove: last should be nil")
// Refill the tree. // Refill the tree.
for k in inserted_keys { for k in inserted_keys {
@@ -130,32 +126,32 @@ test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) {
k := node.key k := node.key
ok := rb.iterator_remove(&iter) ok := rb.iterator_remove(&iter)
tc.expect(t, ok, "iterator/remove: success") testing.expect(t, ok, "iterator/remove: success")
ok = rb.iterator_remove(&iter) ok = rb.iterator_remove(&iter)
tc.expect(t, !ok, "iterator/remove: redundant removes should fail") testing.expect(t, !ok, "iterator/remove: redundant removes should fail")
tc.expect(t, rb.find(&tree, k) == nil, "iterator/remove: node should be gone") testing.expect(t, rb.find(&tree, k) == nil, "iterator/remove: node should be gone")
tc.expect(t, rb.iterator_get(&iter) == nil, "iterator/remove: get should return nil") testing.expect(t, rb.iterator_get(&iter) == nil, "iterator/remove: get should return nil")
// Ensure that iterator_next still works. // Ensure that iterator_next still works.
node, ok = rb.iterator_next(&iter) node, ok = rb.iterator_next(&iter)
tc.expect(t, ok == (rb.len(&tree) > 0), "iterator/remove: next should return false") testing.expect(t, ok == (rb.len(&tree) > 0), "iterator/remove: next should return false")
tc.expect(t, node == rb.first(&tree), "iterator/remove: next should return first") testing.expect(t, node == rb.first(&tree), "iterator/remove: next should return first")
validate_rbtree(t, &tree) validate_rbtree(t, &tree)
} }
tc.expect(t, rb.len(&tree) == entry_count - 1, "iterator/remove: len should drop by 1") testing.expect(t, rb.len(&tree) == entry_count - 1, "iterator/remove: len should drop by 1")
rb.destroy(&tree) rb.destroy(&tree)
tc.expect(t, rb.len(&tree) == 0, "destroy: len should be 0") testing.expect(t, rb.len(&tree) == 0, "destroy: len should be 0")
tc.expect(t, callback_count == 0, fmt.tprintf("remove: on_remove should've been called %v times, it was %v", entry_count, callback_count)) testing.expectf(t, callback_count == 0, "remove: on_remove should've been called %v times, it was %v", entry_count, callback_count)
// print_tree_node(tree._root) // print_tree_node(tree._root)
delete(inserted_map) delete(inserted_map)
delete(inserted_keys) delete(inserted_keys)
tc.expect(t, len(track.allocation_map) == 0, fmt.tprintf("Expected 0 leaks, have %v", len(track.allocation_map))) testing.expectf(t, len(track.allocation_map) == 0, "Expected 0 leaks, have %v", len(track.allocation_map))
tc.expect(t, len(track.bad_free_array) == 0, fmt.tprintf("Expected 0 bad frees, have %v", len(track.bad_free_array))) testing.expectf(t, len(track.bad_free_array) == 0, "Expected 0 bad frees, have %v", len(track.bad_free_array))
return return
} }
@@ -194,7 +190,7 @@ validate_rbtree :: proc(t: ^testing.T, tree: ^$T/rb.Tree($Key, $Value)) {
} }
verify_rbtree_propery_1 :: proc(t: ^testing.T, n: ^$N/rb.Node($Key, $Value)) { verify_rbtree_propery_1 :: proc(t: ^testing.T, n: ^$N/rb.Node($Key, $Value)) {
tc.expect(t, rb.node_color(n) == .Black || rb.node_color(n) == .Red, "Property #1: Each node is either red or black.") testing.expect(t, rb.node_color(n) == .Black || rb.node_color(n) == .Red, "Property #1: Each node is either red or black.")
if n == nil { if n == nil {
return return
} }
@@ -203,14 +199,14 @@ verify_rbtree_propery_1 :: proc(t: ^testing.T, n: ^$N/rb.Node($Key, $Value)) {
} }
verify_rbtree_propery_2 :: proc(t: ^testing.T, root: ^$N/rb.Node($Key, $Value)) { verify_rbtree_propery_2 :: proc(t: ^testing.T, root: ^$N/rb.Node($Key, $Value)) {
tc.expect(t, rb.node_color(root) == .Black, "Property #2: Root node should be black.") testing.expect(t, rb.node_color(root) == .Black, "Property #2: Root node should be black.")
} }
verify_rbtree_propery_4 :: proc(t: ^testing.T, n: ^$N/rb.Node($Key, $Value)) { verify_rbtree_propery_4 :: proc(t: ^testing.T, n: ^$N/rb.Node($Key, $Value)) {
if rb.node_color(n) == .Red { if rb.node_color(n) == .Red {
// A red node's left, right and parent should be black // A red node's left, right and parent should be black
all_black := rb.node_color(n._left) == .Black && rb.node_color(n._right) == .Black && rb.node_color(n._parent) == .Black all_black := rb.node_color(n._left) == .Black && rb.node_color(n._right) == .Black && rb.node_color(n._parent) == .Black
tc.expect(t, all_black, "Property #3: Red node's children + parent must be black.") testing.expect(t, all_black, "Property #3: Red node's children + parent must be black.")
} }
if n == nil { if n == nil {
return return
@@ -233,7 +229,7 @@ verify_rbtree_propery_5_helper :: proc(t: ^testing.T, n: ^$N/rb.Node($Key, $Valu
if path_black_count^ == -1 { if path_black_count^ == -1 {
path_black_count^ = black_count path_black_count^ = black_count
} else { } else {
tc.expect(t, black_count == path_black_count^, "Property #5: Paths from a node to its leaves contain same black count.") testing.expect(t, black_count == path_black_count^, "Property #5: Paths from a node to its leaves contain same black count.")
} }
return return
} }
@@ -241,4 +237,4 @@ verify_rbtree_propery_5_helper :: proc(t: ^testing.T, n: ^$N/rb.Node($Key, $Valu
verify_rbtree_propery_5_helper(t, n._right, black_count, path_black_count) verify_rbtree_propery_5_helper(t, n._right, black_count, path_black_count)
} }
// Properties 4 and 5 together guarantee that no path in the tree is more than about twice as long as any other path, // Properties 4 and 5 together guarantee that no path in the tree is more than about twice as long as any other path,
// which guarantees that it has O(log n) height. // which guarantees that it has O(log n) height.
+35 -32
View File
@@ -3,44 +3,47 @@ package test_core_container
import "core:testing" import "core:testing"
import "core:container/small_array" import "core:container/small_array"
import tc "tests:common"
@(test)
test_small_array :: proc(t: ^testing.T) {
tc.log(t, "Testing small_array")
test_small_array_removes(t)
test_small_array_inject_at(t)
}
@(test) @(test)
test_small_array_removes :: proc(t: ^testing.T) { test_small_array_removes :: proc(t: ^testing.T) {
array: small_array.Small_Array(10, int) array: small_array.Small_Array(10, int)
small_array.append(&array, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) small_array.append(&array, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
small_array.ordered_remove(&array, 0) small_array.ordered_remove(&array, 0)
expect_equal(t, small_array.slice(&array), []int { 1, 2, 3, 4, 5, 6, 7, 8, 9 }) testing.expect(t, slice_equal(small_array.slice(&array), []int { 1, 2, 3, 4, 5, 6, 7, 8, 9 }))
small_array.ordered_remove(&array, 5) small_array.ordered_remove(&array, 5)
expect_equal(t, small_array.slice(&array), []int { 1, 2, 3, 4, 5, 7, 8, 9 }) testing.expect(t, slice_equal(small_array.slice(&array), []int { 1, 2, 3, 4, 5, 7, 8, 9 }))
small_array.ordered_remove(&array, 6) small_array.ordered_remove(&array, 6)
expect_equal(t, small_array.slice(&array), []int { 1, 2, 3, 4, 5, 7, 9 }) testing.expect(t, slice_equal(small_array.slice(&array), []int { 1, 2, 3, 4, 5, 7, 9 }))
small_array.unordered_remove(&array, 0) small_array.unordered_remove(&array, 0)
expect_equal(t, small_array.slice(&array), []int { 9, 2, 3, 4, 5, 7 }) testing.expect(t, slice_equal(small_array.slice(&array), []int { 9, 2, 3, 4, 5, 7 }))
small_array.unordered_remove(&array, 2) small_array.unordered_remove(&array, 2)
expect_equal(t, small_array.slice(&array), []int { 9, 2, 7, 4, 5 }) testing.expect(t, slice_equal(small_array.slice(&array), []int { 9, 2, 7, 4, 5 }))
small_array.unordered_remove(&array, 4) small_array.unordered_remove(&array, 4)
expect_equal(t, small_array.slice(&array), []int { 9, 2, 7, 4 }) testing.expect(t, slice_equal(small_array.slice(&array), []int { 9, 2, 7, 4 }))
} }
@(test) @(test)
test_small_array_inject_at :: proc(t: ^testing.T) { test_small_array_inject_at :: proc(t: ^testing.T) {
array: small_array.Small_Array(13, int) array: small_array.Small_Array(13, int)
small_array.append(&array, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) small_array.append(&array, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
tc.expect(t, small_array.inject_at(&array, 0, 0), "Expected to be able to inject into small array") testing.expect(t, small_array.inject_at(&array, 0, 0), "Expected to be able to inject into small array")
expect_equal(t, small_array.slice(&array), []int { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }) testing.expect(t, slice_equal(small_array.slice(&array), []int { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }))
tc.expect(t, small_array.inject_at(&array, 0, 5), "Expected to be able to inject into small array") testing.expect(t, small_array.inject_at(&array, 0, 5), "Expected to be able to inject into small array")
expect_equal(t, small_array.slice(&array), []int { 0, 0, 1, 2, 3, 0, 4, 5, 6, 7, 8, 9 }) testing.expect(t, slice_equal(small_array.slice(&array), []int { 0, 0, 1, 2, 3, 0, 4, 5, 6, 7, 8, 9 }))
tc.expect(t, small_array.inject_at(&array, 0, small_array.len(array)), "Expected to be able to inject into small array") testing.expect(t, small_array.inject_at(&array, 0, small_array.len(array)), "Expected to be able to inject into small array")
expect_equal(t, small_array.slice(&array), []int { 0, 0, 1, 2, 3, 0, 4, 5, 6, 7, 8, 9, 0 }) testing.expect(t, slice_equal(small_array.slice(&array), []int { 0, 0, 1, 2, 3, 0, 4, 5, 6, 7, 8, 9, 0 }))
}
slice_equal :: proc(a, b: []int) -> bool {
if len(a) != len(b) {
return false
}
for a, i in a {
if b[i] != a {
return false
}
}
return true
} }
+28 -68
View File
@@ -13,41 +13,20 @@ package test_core_crypto
*/ */
import "core:encoding/hex" import "core:encoding/hex"
import "core:fmt"
import "core:mem" import "core:mem"
import "core:testing" import "core:testing"
import "base:runtime"
import "core:log"
import "core:crypto" import "core:crypto"
import "core:crypto/chacha20" import "core:crypto/chacha20"
import "core:crypto/chacha20poly1305" import "core:crypto/chacha20poly1305"
import tc "tests:common"
main :: proc() {
t := testing.T{}
test_rand_bytes(&t)
test_hash(&t)
test_mac(&t)
test_kdf(&t) // After hash/mac tests because those should pass first.
test_ecc25519(&t)
test_aes(&t)
test_chacha20(&t)
test_chacha20poly1305(&t)
test_sha3_variants(&t)
bench_crypto(&t)
tc.report(&t)
}
_PLAINTEXT_SUNSCREEN_STR := "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it." _PLAINTEXT_SUNSCREEN_STR := "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."
@(test) @(test)
test_chacha20 :: proc(t: ^testing.T) { test_chacha20 :: proc(t: ^testing.T) {
tc.log(t, "Testing (X)ChaCha20") runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
// Test cases taken from RFC 8439, and draft-irtf-cfrg-xchacha-03 // Test cases taken from RFC 8439, and draft-irtf-cfrg-xchacha-03
plaintext := transmute([]byte)(_PLAINTEXT_SUNSCREEN_STR) plaintext := transmute([]byte)(_PLAINTEXT_SUNSCREEN_STR)
@@ -90,14 +69,12 @@ test_chacha20 :: proc(t: ^testing.T) {
chacha20.xor_bytes(&ctx, derived_ciphertext[:], plaintext[:]) chacha20.xor_bytes(&ctx, derived_ciphertext[:], plaintext[:])
derived_ciphertext_str := string(hex.encode(derived_ciphertext[:], context.temp_allocator)) derived_ciphertext_str := string(hex.encode(derived_ciphertext[:], context.temp_allocator))
tc.expect( testing.expectf(
t, t,
derived_ciphertext_str == ciphertext_str, derived_ciphertext_str == ciphertext_str,
fmt.tprintf( "Expected %s for xor_bytes(plaintext_str), but got %s instead",
"Expected %s for xor_bytes(plaintext_str), but got %s instead", ciphertext_str,
ciphertext_str, derived_ciphertext_str,
derived_ciphertext_str,
),
) )
xkey := [chacha20.KEY_SIZE]byte { xkey := [chacha20.KEY_SIZE]byte {
@@ -137,21 +114,17 @@ test_chacha20 :: proc(t: ^testing.T) {
chacha20.xor_bytes(&ctx, derived_ciphertext[:], plaintext[:]) chacha20.xor_bytes(&ctx, derived_ciphertext[:], plaintext[:])
derived_ciphertext_str = string(hex.encode(derived_ciphertext[:], context.temp_allocator)) derived_ciphertext_str = string(hex.encode(derived_ciphertext[:], context.temp_allocator))
tc.expect( testing.expectf(
t, t,
derived_ciphertext_str == xciphertext_str, derived_ciphertext_str == xciphertext_str,
fmt.tprintf( "Expected %s for xor_bytes(plaintext_str), but got %s instead",
"Expected %s for xor_bytes(plaintext_str), but got %s instead", xciphertext_str,
xciphertext_str, derived_ciphertext_str,
derived_ciphertext_str,
),
) )
} }
@(test) @(test)
test_chacha20poly1305 :: proc(t: ^testing.T) { test_chacha20poly1305 :: proc(t: ^testing.T) {
tc.log(t, "Testing chacha20poly1205")
plaintext := transmute([]byte)(_PLAINTEXT_SUNSCREEN_STR) plaintext := transmute([]byte)(_PLAINTEXT_SUNSCREEN_STR)
aad := [12]byte { aad := [12]byte {
@@ -209,25 +182,21 @@ test_chacha20poly1305 :: proc(t: ^testing.T) {
) )
derived_ciphertext_str := string(hex.encode(derived_ciphertext[:], context.temp_allocator)) derived_ciphertext_str := string(hex.encode(derived_ciphertext[:], context.temp_allocator))
tc.expect( testing.expectf(
t, t,
derived_ciphertext_str == ciphertext_str, derived_ciphertext_str == ciphertext_str,
fmt.tprintf( "Expected ciphertext %s for encrypt(aad, plaintext), but got %s instead",
"Expected ciphertext %s for encrypt(aad, plaintext), but got %s instead", ciphertext_str,
ciphertext_str, derived_ciphertext_str,
derived_ciphertext_str,
),
) )
derived_tag_str := string(hex.encode(derived_tag[:], context.temp_allocator)) derived_tag_str := string(hex.encode(derived_tag[:], context.temp_allocator))
tc.expect( testing.expectf(
t, t,
derived_tag_str == tag_str, derived_tag_str == tag_str,
fmt.tprintf( "Expected tag %s for encrypt(aad, plaintext), but got %s instead",
"Expected tag %s for encrypt(aad, plaintext), but got %s instead", tag_str,
tag_str, derived_tag_str,
derived_tag_str,
),
) )
derived_plaintext: [114]byte derived_plaintext: [114]byte
@@ -240,15 +209,13 @@ test_chacha20poly1305 :: proc(t: ^testing.T) {
ciphertext[:], ciphertext[:],
) )
derived_plaintext_str := string(derived_plaintext[:]) derived_plaintext_str := string(derived_plaintext[:])
tc.expect(t, ok, "Expected true for decrypt(tag, aad, ciphertext)") testing.expect(t, ok, "Expected true for decrypt(tag, aad, ciphertext)")
tc.expect( testing.expectf(
t, t,
derived_plaintext_str == _PLAINTEXT_SUNSCREEN_STR, derived_plaintext_str == _PLAINTEXT_SUNSCREEN_STR,
fmt.tprintf( "Expected plaintext %s for decrypt(tag, aad, ciphertext), but got %s instead",
"Expected plaintext %s for decrypt(tag, aad, ciphertext), but got %s instead", _PLAINTEXT_SUNSCREEN_STR,
_PLAINTEXT_SUNSCREEN_STR, derived_plaintext_str,
derived_plaintext_str,
),
) )
derived_ciphertext[0] ~= 0xa5 derived_ciphertext[0] ~= 0xa5
@@ -260,7 +227,7 @@ test_chacha20poly1305 :: proc(t: ^testing.T) {
aad[:], aad[:],
derived_ciphertext[:], derived_ciphertext[:],
) )
tc.expect(t, !ok, "Expected false for decrypt(tag, aad, corrupted_ciphertext)") testing.expect(t, !ok, "Expected false for decrypt(tag, aad, corrupted_ciphertext)")
aad[0] ~= 0xa5 aad[0] ~= 0xa5
ok = chacha20poly1305.decrypt( ok = chacha20poly1305.decrypt(
@@ -271,15 +238,13 @@ test_chacha20poly1305 :: proc(t: ^testing.T) {
aad[:], aad[:],
ciphertext[:], ciphertext[:],
) )
tc.expect(t, !ok, "Expected false for decrypt(tag, corrupted_aad, ciphertext)") testing.expect(t, !ok, "Expected false for decrypt(tag, corrupted_aad, ciphertext)")
} }
@(test) @(test)
test_rand_bytes :: proc(t: ^testing.T) { test_rand_bytes :: proc(t: ^testing.T) {
tc.log(t, "Testing rand_bytes")
if !crypto.HAS_RAND_BYTES { if !crypto.HAS_RAND_BYTES {
tc.log(t, "rand_bytes not supported - skipping") log.info("rand_bytes not supported - skipping")
return return
} }
@@ -307,10 +272,5 @@ test_rand_bytes :: proc(t: ^testing.T) {
break break
} }
} }
testing.expect(t, seems_ok, "Expected to randomize the head and tail of the buffer within a handful of attempts")
tc.expect(
t,
seems_ok,
"Expected to randomize the head and tail of the buffer within a handful of attempts",
)
} }
+55 -71
View File
@@ -2,21 +2,20 @@ package test_core_crypto
import "base:runtime" import "base:runtime"
import "core:encoding/hex" import "core:encoding/hex"
import "core:fmt" import "core:log"
import "core:testing" import "core:testing"
import "core:crypto/aes" import "core:crypto/aes"
import "core:crypto/sha2" import "core:crypto/sha2"
import tc "tests:common"
@(test) @(test)
test_aes :: proc(t: ^testing.T) { test_aes :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
tc.log(t, "Testing AES") log.info("Testing AES")
impls := make([dynamic]aes.Implementation, 0, 2) impls := make([dynamic]aes.Implementation, 0, 2)
defer delete(impls)
append(&impls, aes.Implementation.Portable) append(&impls, aes.Implementation.Portable)
if aes.is_hardware_accelerated() { if aes.is_hardware_accelerated() {
append(&impls, aes.Implementation.Hardware) append(&impls, aes.Implementation.Hardware)
@@ -29,9 +28,8 @@ test_aes :: proc(t: ^testing.T) {
} }
} }
@(test)
test_aes_ecb :: proc(t: ^testing.T, impl: aes.Implementation) { test_aes_ecb :: proc(t: ^testing.T, impl: aes.Implementation) {
tc.log(t, fmt.tprintf("Testing AES-ECB/%v", impl)) log.infof("Testing AES-ECB/%v", impl)
test_vectors := []struct { test_vectors := []struct {
key: string, key: string,
@@ -111,39 +109,34 @@ test_aes_ecb :: proc(t: ^testing.T, impl: aes.Implementation) {
aes.encrypt_ecb(&ctx, dst[:], plaintext) aes.encrypt_ecb(&ctx, dst[:], plaintext)
dst_str := string(hex.encode(dst[:], context.temp_allocator)) dst_str := string(hex.encode(dst[:], context.temp_allocator))
tc.expect( testing.expectf(
t, t,
dst_str == v.ciphertext, dst_str == v.ciphertext,
fmt.tprintf( "AES-ECB/%v: Expected: %s for encrypt(%s, %s), but got %s instead",
"AES-ECB/%v: Expected: %s for encrypt(%s, %s), but got %s instead", impl,
impl, v.ciphertext,
v.ciphertext, v.key,
v.key, v.plaintext,
v.plaintext, dst_str,
dst_str,
),
) )
aes.decrypt_ecb(&ctx, dst[:], ciphertext) aes.decrypt_ecb(&ctx, dst[:], ciphertext)
dst_str = string(hex.encode(dst[:], context.temp_allocator)) dst_str = string(hex.encode(dst[:], context.temp_allocator))
tc.expect( testing.expectf(
t, t,
dst_str == v.plaintext, dst_str == v.plaintext,
fmt.tprintf( "AES-ECB/%v: Expected: %s for decrypt(%s, %s), but got %s instead",
"AES-ECB/%v: Expected: %s for decrypt(%s, %s), but got %s instead", impl,
impl, v.plaintext,
v.plaintext, v.key,
v.key, v.ciphertext,
v.ciphertext, dst_str,
dst_str,
),
) )
} }
} }
@(test)
test_aes_ctr :: proc(t: ^testing.T, impl: aes.Implementation) { test_aes_ctr :: proc(t: ^testing.T, impl: aes.Implementation) {
tc.log(t, fmt.tprintf("Testing AES-CTR/%v", impl)) log.infof("Testing AES-CTR/%v", impl)
test_vectors := []struct { test_vectors := []struct {
key: string, key: string,
@@ -185,18 +178,16 @@ test_aes_ctr :: proc(t: ^testing.T, impl: aes.Implementation) {
aes.xor_bytes_ctr(&ctx, dst, plaintext) aes.xor_bytes_ctr(&ctx, dst, plaintext)
dst_str := string(hex.encode(dst[:], context.temp_allocator)) dst_str := string(hex.encode(dst[:], context.temp_allocator))
tc.expect( testing.expectf(
t, t,
dst_str == v.ciphertext, dst_str == v.ciphertext,
fmt.tprintf( "AES-CTR/%v: Expected: %s for encrypt(%s, %s, %s), but got %s instead",
"AES-CTR/%v: Expected: %s for encrypt(%s, %s, %s), but got %s instead", impl,
impl, v.ciphertext,
v.ciphertext, v.key,
v.key, v.iv,
v.iv, v.plaintext,
v.plaintext, dst_str,
dst_str,
),
) )
} }
@@ -224,21 +215,18 @@ test_aes_ctr :: proc(t: ^testing.T, impl: aes.Implementation) {
digest_str := string(hex.encode(digest[:], context.temp_allocator)) digest_str := string(hex.encode(digest[:], context.temp_allocator))
expected_digest_str := "d4445343afeb9d1237f95b10d00358aed4c1d7d57c9fe480cd0afb5e2ffd448c" expected_digest_str := "d4445343afeb9d1237f95b10d00358aed4c1d7d57c9fe480cd0afb5e2ffd448c"
tc.expect( testing.expectf(
t, t,
expected_digest_str == digest_str, expected_digest_str == digest_str,
fmt.tprintf( "AES-CTR/%v: Expected %s for keystream digest, but got %s instead",
"AES-CTR/%v: Expected %s for keystream digest, but got %s instead", impl,
impl, expected_digest_str,
expected_digest_str, digest_str,
digest_str,
),
) )
} }
@(test)
test_aes_gcm :: proc(t: ^testing.T, impl: aes.Implementation) { test_aes_gcm :: proc(t: ^testing.T, impl: aes.Implementation) {
tc.log(t, fmt.tprintf("Testing AES-GCM/%v", impl)) log.infof("Testing AES-GCM/%v", impl)
// NIST did a reorg of their site, so the source of the test vectors // NIST did a reorg of their site, so the source of the test vectors
// is only available from an archive. The commented out tests are // is only available from an archive. The commented out tests are
@@ -422,41 +410,37 @@ test_aes_gcm :: proc(t: ^testing.T, impl: aes.Implementation) {
dst_str := string(hex.encode(dst[:], context.temp_allocator)) dst_str := string(hex.encode(dst[:], context.temp_allocator))
tag_str := string(hex.encode(tag_[:], context.temp_allocator)) tag_str := string(hex.encode(tag_[:], context.temp_allocator))
tc.expect( testing.expectf(
t, t,
dst_str == v.ciphertext && tag_str == v.tag, dst_str == v.ciphertext && tag_str == v.tag,
fmt.tprintf( "AES-GCM/%v: Expected: (%s, %s) for seal(%s, %s, %s, %s), but got (%s, %s) instead",
"AES-GCM/%v: Expected: (%s, %s) for seal(%s, %s, %s, %s), but got (%s, %s) instead", impl,
impl, v.ciphertext,
v.ciphertext, v.tag,
v.tag, v.key,
v.key, v.iv,
v.iv, v.aad,
v.aad, v.plaintext,
v.plaintext, dst_str,
dst_str, tag_str,
tag_str,
),
) )
ok := aes.open_gcm(&ctx, dst, iv, aad, ciphertext, tag) ok := aes.open_gcm(&ctx, dst, iv, aad, ciphertext, tag)
dst_str = string(hex.encode(dst[:], context.temp_allocator)) dst_str = string(hex.encode(dst[:], context.temp_allocator))
tc.expect( testing.expectf(
t, t,
ok && dst_str == v.plaintext, ok && dst_str == v.plaintext,
fmt.tprintf( "AES-GCM/%v: Expected: (%s, true) for open(%s, %s, %s, %s, %s), but got (%s, %s) instead",
"AES-GCM/%v: Expected: (%s, true) for open(%s, %s, %s, %s, %s), but got (%s, %s) instead", impl,
impl, v.plaintext,
v.plaintext, v.key,
v.key, v.iv,
v.iv, v.aad,
v.aad, v.ciphertext,
v.ciphertext, v.tag,
v.tag, dst_str,
dst_str, ok,
ok,
),
) )
} }
} }
+113 -180
View File
@@ -1,8 +1,6 @@
package test_core_crypto package test_core_crypto
import "base:runtime"
import "core:encoding/hex" import "core:encoding/hex"
import "core:fmt"
import "core:testing" import "core:testing"
import field "core:crypto/_fiat/field_curve25519" import field "core:crypto/_fiat/field_curve25519"
@@ -10,25 +8,8 @@ import "core:crypto/ed25519"
import "core:crypto/ristretto255" import "core:crypto/ristretto255"
import "core:crypto/x25519" import "core:crypto/x25519"
import tc "tests:common"
@(test)
test_ecc25519 :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
tc.log(t, "Testing curve25519 ECC")
test_sqrt_ratio_m1(t)
test_ristretto255(t)
test_ed25519(t)
test_x25519(t)
}
@(test) @(test)
test_sqrt_ratio_m1 :: proc(t: ^testing.T) { test_sqrt_ratio_m1 :: proc(t: ^testing.T) {
tc.log(t, "Testing sqrt_ratio_m1")
test_vectors := []struct { test_vectors := []struct {
u: string, u: string,
v: string, v: string,
@@ -90,25 +71,21 @@ test_sqrt_ratio_m1 :: proc(t: ^testing.T) {
field.fe_relax_cast(&vee), field.fe_relax_cast(&vee),
) )
tc.expect( testing.expectf(
t, t,
(was_square == 1) == v.was_square && field.fe_equal_bytes(&r, r_) == 1, (was_square == 1) == v.was_square && field.fe_equal_bytes(&r, r_) == 1,
fmt.tprintf( "Expected (%v, %s) for SQRT_RATIO_M1(%s, %s), got %s",
"Expected (%v, %s) for SQRT_RATIO_M1(%s, %s), got %s", v.was_square,
v.was_square, v.r,
v.r, v.u,
v.u, v.v,
v.v, fe_str(&r),
fe_str(&r),
),
) )
} }
} }
@(test) @(test)
test_ristretto255 :: proc(t: ^testing.T) { test_ristretto255 :: proc(t: ^testing.T) {
tc.log(t, "Testing ristretto255")
ge_gen: ristretto255.Group_Element ge_gen: ristretto255.Group_Element
ristretto255.ge_generator(&ge_gen) ristretto255.ge_generator(&ge_gen)
@@ -158,7 +135,7 @@ test_ristretto255 :: proc(t: ^testing.T) {
ge: ristretto255.Group_Element ge: ristretto255.Group_Element
ok := ristretto255.ge_set_bytes(&ge, b) ok := ristretto255.ge_set_bytes(&ge, b)
tc.expect(t, !ok, fmt.tprintf("Expected false for %s", x)) testing.expectf(t, !ok, "Expected false for %s", x)
} }
generator_multiples := []string { generator_multiples := []string {
@@ -185,22 +162,20 @@ test_ristretto255 :: proc(t: ^testing.T) {
ge := &ges[i] ge := &ges[i]
ok := ristretto255.ge_set_bytes(ge, b) ok := ristretto255.ge_set_bytes(ge, b)
tc.expect(t, ok, fmt.tprintf("Expected true for %s", x)) testing.expectf(t, ok, "Expected true for %s", x)
x_check := ge_str(ge) x_check := ge_str(ge)
tc.expect( testing.expectf(
t, t,
x == x_check, x == x_check,
fmt.tprintf( "Expected %s (round-trip) but got %s instead",
"Expected %s (round-trip) but got %s instead", x,
x, x_check,
x_check,
),
) )
if i == 1 { if i == 1 {
tc.expect( testing.expect(
t, t,
ristretto255.ge_equal(ge, &ge_gen) == 1, ristretto255.ge_equal(ge, &ge_gen) == 1,
"Expected element 1 to be the generator", "Expected element 1 to be the generator",
@@ -217,41 +192,35 @@ test_ristretto255 :: proc(t: ^testing.T) {
ristretto255.ge_scalarmult_generator(&ge_check, &sc) ristretto255.ge_scalarmult_generator(&ge_check, &sc)
x_check := ge_str(&ge_check) x_check := ge_str(&ge_check)
tc.expect( testing.expectf(
t, t,
x_check == generator_multiples[i], x_check == generator_multiples[i],
fmt.tprintf( "Expected %s for G * %d (specialized), got %s",
"Expected %s for G * %d (specialized), got %s", generator_multiples[i],
generator_multiples[i], i,
i, x_check,
x_check,
),
) )
ristretto255.ge_scalarmult(&ge_check, &ges[1], &sc) ristretto255.ge_scalarmult(&ge_check, &ges[1], &sc)
x_check = ge_str(&ge_check) x_check = ge_str(&ge_check)
tc.expect( testing.expectf(
t, t,
x_check == generator_multiples[i], x_check == generator_multiples[i],
fmt.tprintf( "Expected %s for G * %d (generic), got %s (slow compare)",
"Expected %s for G * %d (generic), got %s (slow compare)", generator_multiples[i],
generator_multiples[i], i,
i, x_check,
x_check,
),
) )
ristretto255.ge_scalarmult_vartime(&ge_check, &ges[1], &sc) ristretto255.ge_scalarmult_vartime(&ge_check, &ges[1], &sc)
x_check = ge_str(&ge_check) x_check = ge_str(&ge_check)
tc.expect( testing.expectf(
t, t,
x_check == generator_multiples[i], x_check == generator_multiples[i],
fmt.tprintf( "Expected %s for G * %d (generic vartime), got %s (slow compare)",
"Expected %s for G * %d (generic vartime), got %s (slow compare)", generator_multiples[i],
generator_multiples[i], i,
i, x_check,
x_check,
),
) )
switch i { switch i {
@@ -261,28 +230,24 @@ test_ristretto255 :: proc(t: ^testing.T) {
ristretto255.ge_add(&ge_check, ge_prev, &ge_gen) ristretto255.ge_add(&ge_check, ge_prev, &ge_gen)
x_check = ge_str(&ge_check) x_check = ge_str(&ge_check)
tc.expect( testing.expectf(
t, t,
x_check == generator_multiples[i], x_check == generator_multiples[i],
fmt.tprintf( "Expected %s for ges[%d] + ges[%d], got %s (slow compare)",
"Expected %s for ges[%d] + ges[%d], got %s (slow compare)", generator_multiples[i],
generator_multiples[i], i-1,
i-1, 1,
1, x_check,
x_check,
),
) )
tc.expect( testing.expectf(
t, t,
ristretto255.ge_equal(&ges[i], &ge_check) == 1, ristretto255.ge_equal(&ges[i], &ge_check) == 1,
fmt.tprintf( "Expected %s for ges[%d] + ges[%d], got %s (fast compare)",
"Expected %s for ges[%d] + ges[%d], got %s (fast compare)", generator_multiples[i],
generator_multiples[i], i-1,
i-1, 1,
1, x_check,
x_check,
),
) )
} }
} }
@@ -344,22 +309,18 @@ test_ristretto255 :: proc(t: ^testing.T) {
ristretto255.ge_set_wide_bytes(&ge, in_bytes) ristretto255.ge_set_wide_bytes(&ge, in_bytes)
ge_check := ge_str(&ge) ge_check := ge_str(&ge)
tc.expect( testing.expectf(
t, t,
ge_check == v.output, ge_check == v.output,
fmt.tprintf( "Expected %s for %s, got %s",
"Expected %s for %s, got %s", v.output,
v.output, ge_check,
ge_check,
),
) )
} }
} }
@(test) @(test)
test_ed25519 :: proc(t: ^testing.T) { test_ed25519 :: proc(t: ^testing.T) {
tc.log(t, "Testing ed25519")
test_vectors_rfc := []struct { test_vectors_rfc := []struct {
priv_key: string, priv_key: string,
pub_key: string, pub_key: string,
@@ -401,87 +362,73 @@ test_ed25519 :: proc(t: ^testing.T) {
priv_key: ed25519.Private_Key priv_key: ed25519.Private_Key
ok := ed25519.private_key_set_bytes(&priv_key, priv_bytes) ok := ed25519.private_key_set_bytes(&priv_key, priv_bytes)
tc.expect( testing.expectf(
t, t,
ok, ok,
fmt.tprintf( "Expected %s to be a valid private key",
"Expected %s to be a valid private key", v.priv_key,
v.priv_key,
),
) )
key_bytes: [32]byte key_bytes: [32]byte
ed25519.private_key_bytes(&priv_key, key_bytes[:]) ed25519.private_key_bytes(&priv_key, key_bytes[:])
tc.expect( testing.expectf(
t, t,
ok, ok,
fmt.tprintf( "Expected private key %s round-trip, got %s",
"Expected private key %s round-trip, got %s", v.priv_key,
v.priv_key, string(hex.encode(key_bytes[:], context.temp_allocator)),
string(hex.encode(key_bytes[:], context.temp_allocator)),
),
) )
pub_key: ed25519.Public_Key pub_key: ed25519.Public_Key
ok = ed25519.public_key_set_bytes(&pub_key, pub_bytes) ok = ed25519.public_key_set_bytes(&pub_key, pub_bytes)
tc.expect( testing.expectf(
t, t,
ok, ok,
fmt.tprintf( "Expected %s to be a valid public key (priv->pub: %s)",
"Expected %s to be a valid public key (priv->pub: %s)", v.pub_key,
v.pub_key, string(hex.encode(priv_key._pub_key._b[:], context.temp_allocator)),
string(hex.encode(priv_key._pub_key._b[:], context.temp_allocator)),
),
) )
ed25519.public_key_bytes(&pub_key, key_bytes[:]) ed25519.public_key_bytes(&pub_key, key_bytes[:])
tc.expect( testing.expectf(
t, t,
ok, ok,
fmt.tprintf( "Expected public key %s round-trip, got %s",
"Expected public key %s round-trip, got %s", v.pub_key,
v.pub_key, string(hex.encode(key_bytes[:], context.temp_allocator)),
string(hex.encode(key_bytes[:], context.temp_allocator)),
),
) )
sig: [ed25519.SIGNATURE_SIZE]byte sig: [ed25519.SIGNATURE_SIZE]byte
ed25519.sign(&priv_key, msg_bytes, sig[:]) ed25519.sign(&priv_key, msg_bytes, sig[:])
x := string(hex.encode(sig[:], context.temp_allocator)) x := string(hex.encode(sig[:], context.temp_allocator))
tc.expect( testing.expectf(
t, t,
x == v.sig, x == v.sig,
fmt.tprintf( "Expected %s for sign(%s, %s), got %s",
"Expected %s for sign(%s, %s), got %s", v.sig,
v.sig, v.priv_key,
v.priv_key, v.msg,
v.msg, x,
x,
),
) )
ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes) ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes)
tc.expect( testing.expectf(
t, t,
ok, ok,
fmt.tprintf( "Expected true for verify(%s, %s, %s)",
"Expected true for verify(%s, %s, %s)", v.pub_key,
v.pub_key, v.msg,
v.msg, v.sig,
v.sig,
),
) )
ok = ed25519.verify(&priv_key._pub_key, msg_bytes, sig_bytes) ok = ed25519.verify(&priv_key._pub_key, msg_bytes, sig_bytes)
tc.expect( testing.expectf(
t, t,
ok, ok,
fmt.tprintf( "Expected true for verify(pub(%s), %s %s)",
"Expected true for verify(pub(%s), %s %s)", v.priv_key,
v.priv_key, v.msg,
v.msg, v.sig,
v.sig,
),
) )
// Corrupt the message and make sure verification fails. // Corrupt the message and make sure verification fails.
@@ -493,15 +440,13 @@ test_ed25519 :: proc(t: ^testing.T) {
msg_bytes[0] = msg_bytes[0] ~ 69 msg_bytes[0] = msg_bytes[0] ~ 69
} }
ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes) ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes)
tc.expect( testing.expectf(
t, t,
ok == false, ok == false,
fmt.tprintf( "Expected false for verify(%s, %s (corrupted), %s)",
"Expected false for verify(%s, %s (corrupted), %s)", v.pub_key,
v.pub_key, v.msg,
v.msg, v.sig,
v.sig,
),
) )
} }
@@ -634,15 +579,13 @@ test_ed25519 :: proc(t: ^testing.T) {
pub_key: ed25519.Public_Key pub_key: ed25519.Public_Key
ok := ed25519.public_key_set_bytes(&pub_key, pub_bytes) ok := ed25519.public_key_set_bytes(&pub_key, pub_bytes)
tc.expect( testing.expectf(
t, t,
ok == v.pub_key_ok, ok == v.pub_key_ok,
fmt.tprintf( "speccheck/%d: Expected %s to be a (in)valid public key, got %v",
"speccheck/%d: Expected %s to be a (in)valid public key, got %v", i,
i, v.pub_key,
v.pub_key, ok,
ok,
),
) )
// If A is rejected for being non-canonical, skip signature check. // If A is rejected for being non-canonical, skip signature check.
@@ -651,17 +594,15 @@ test_ed25519 :: proc(t: ^testing.T) {
} }
ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes) ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes)
tc.expect( testing.expectf(
t, t,
ok == v.sig_ok, ok == v.sig_ok,
fmt.tprintf( "speccheck/%d Expected %v for verify(%s, %s, %s)",
"speccheck/%d Expected %v for verify(%s, %s, %s)", i,
i, v.sig_ok,
v.sig_ok, v.pub_key,
v.pub_key, v.msg,
v.msg, v.sig,
v.sig,
),
) )
// If the signature is accepted, skip the relaxed signature check. // If the signature is accepted, skip the relaxed signature check.
@@ -670,25 +611,21 @@ test_ed25519 :: proc(t: ^testing.T) {
} }
ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes, true) ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes, true)
tc.expect( testing.expectf(
t, t,
ok == v.sig_ok_relaxed, ok == v.sig_ok_relaxed,
fmt.tprintf( "speccheck/%d Expected %v for verify(%s, %s, %s, true)",
"speccheck/%d Expected %v for verify(%s, %s, %s, true)", i,
i, v.sig_ok_relaxed,
v.sig_ok_relaxed, v.pub_key,
v.pub_key, v.msg,
v.msg, v.sig,
v.sig,
),
) )
} }
} }
@(test) @(test)
test_x25519 :: proc(t: ^testing.T) { test_x25519 :: proc(t: ^testing.T) {
tc.log(t, "Testing X25519")
// Local copy of this so that the base point doesn't need to be exported. // Local copy of this so that the base point doesn't need to be exported.
_BASE_POINT: [32]byte = { _BASE_POINT: [32]byte = {
9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -720,17 +657,15 @@ test_x25519 :: proc(t: ^testing.T) {
x25519.scalarmult(derived_point[:], scalar[:], point[:]) x25519.scalarmult(derived_point[:], scalar[:], point[:])
derived_point_str := string(hex.encode(derived_point[:], context.temp_allocator)) derived_point_str := string(hex.encode(derived_point[:], context.temp_allocator))
tc.expect( testing.expectf(
t, t,
derived_point_str == v.product, derived_point_str == v.product,
fmt.tprintf( "Expected %s for %s * %s, but got %s instead",
"Expected %s for %s * %s, but got %s instead", v.product,
v.product, v.scalar,
v.scalar, v.point,
v.point, derived_point_str,
derived_point_str, )
),
)
// Abuse the test vectors to sanity-check the scalar-basepoint multiply. // Abuse the test vectors to sanity-check the scalar-basepoint multiply.
p1, p2: [x25519.POINT_SIZE]byte p1, p2: [x25519.POINT_SIZE]byte
@@ -738,15 +673,13 @@ test_x25519 :: proc(t: ^testing.T) {
x25519.scalarmult(p2[:], scalar[:], _BASE_POINT[:]) x25519.scalarmult(p2[:], scalar[:], _BASE_POINT[:])
p1_str := string(hex.encode(p1[:], context.temp_allocator)) p1_str := string(hex.encode(p1[:], context.temp_allocator))
p2_str := string(hex.encode(p2[:], context.temp_allocator)) p2_str := string(hex.encode(p2[:], context.temp_allocator))
tc.expect( testing.expectf(
t, t,
p1_str == p2_str, p1_str == p2_str,
fmt.tprintf( "Expected %s for %s * basepoint, but got %s instead",
"Expected %s for %s * basepoint, but got %s instead", p2_str,
p2_str, v.scalar,
v.scalar, p1_str,
p1_str,
),
) )
} }
} }
@@ -763,4 +696,4 @@ fe_str :: proc(fe: ^field.Tight_Field_Element) -> string {
b: [32]byte b: [32]byte
field.fe_to_bytes(&b, fe) field.fe_to_bytes(&b, fe)
return string(hex.encode(b[:], context.temp_allocator)) return string(hex.encode(b[:], context.temp_allocator))
} }
+39 -59
View File
@@ -3,23 +3,17 @@ package test_core_crypto
import "base:runtime" import "base:runtime"
import "core:bytes" import "core:bytes"
import "core:encoding/hex" import "core:encoding/hex"
import "core:fmt"
import "core:strings" import "core:strings"
import "core:testing" import "core:testing"
import "core:crypto/hash" import "core:crypto/hash"
import tc "tests:common"
@(test) @(test)
test_hash :: proc(t: ^testing.T) { test_hash :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
tc.log(t, "Testing Hashes")
// TODO: // TODO:
// - Stick the test vectors in a JSON file or something. // - Stick the test vectors in a JSON file or something.
data_1_000_000_a := strings.repeat("a", 1_000_000) data_1_000_000_a := strings.repeat("a", 1_000_000, context.temp_allocator)
digest: [hash.MAX_DIGEST_SIZE]byte digest: [hash.MAX_DIGEST_SIZE]byte
test_vectors := []struct{ test_vectors := []struct{
@@ -496,16 +490,14 @@ test_hash :: proc(t: ^testing.T) {
dst_str := string(hex.encode(dst, context.temp_allocator)) dst_str := string(hex.encode(dst, context.temp_allocator))
tc.expect( testing.expectf(
t, t,
dst_str == v.hash, dst_str == v.hash,
fmt.tprintf( "%s/incremental: Expected: %s for input of %s, but got %s instead",
"%s/incremental: Expected: %s for input of %s, but got %s instead", algo_name,
algo_name, v.hash,
v.hash, v.str,
v.str, dst_str,
dst_str,
),
) )
} }
@@ -521,25 +513,21 @@ test_hash :: proc(t: ^testing.T) {
// still correct. // still correct.
digest_sz := hash.DIGEST_SIZES[algo] digest_sz := hash.DIGEST_SIZES[algo]
block_sz := hash.BLOCK_SIZES[algo] block_sz := hash.BLOCK_SIZES[algo]
tc.expect( testing.expectf(
t, t,
digest_sz <= hash.MAX_DIGEST_SIZE, digest_sz <= hash.MAX_DIGEST_SIZE,
fmt.tprintf( "%s: Digest size %d exceeds max %d",
"%s: Digest size %d exceeds max %d", algo_name,
algo_name, digest_sz,
digest_sz, hash.MAX_DIGEST_SIZE,
hash.MAX_DIGEST_SIZE,
),
) )
tc.expect( testing.expectf(
t, t,
block_sz <= hash.MAX_BLOCK_SIZE, block_sz <= hash.MAX_BLOCK_SIZE,
fmt.tprintf( "%s: Block size %d exceeds max %d",
"%s: Block size %d exceeds max %d", algo_name,
algo_name, block_sz,
block_sz, hash.MAX_BLOCK_SIZE,
hash.MAX_BLOCK_SIZE,
),
) )
// Exercise most of the happy-path for the high level interface. // Exercise most of the happy-path for the high level interface.
@@ -553,15 +541,13 @@ test_hash :: proc(t: ^testing.T) {
a_str := string(hex.encode(digest_a, context.temp_allocator)) a_str := string(hex.encode(digest_a, context.temp_allocator))
b_str := string(hex.encode(digest_b, context.temp_allocator)) b_str := string(hex.encode(digest_b, context.temp_allocator))
tc.expect( testing.expectf(
t, t,
a_str == b_str, a_str == b_str,
fmt.tprintf( "%s/cmp: Expected: %s (hash_stream) == %s (hash_bytes)",
"%s/cmp: Expected: %s (hash_stream) == %s (hash_bytes)", algo_name,
algo_name, a_str,
a_str, b_str,
b_str,
),
) )
// Exercise the rolling digest functionality, which also covers // Exercise the rolling digest functionality, which also covers
@@ -571,25 +557,21 @@ test_hash :: proc(t: ^testing.T) {
api_algo := hash.algorithm(&ctx) api_algo := hash.algorithm(&ctx)
api_digest_size := hash.digest_size(&ctx) api_digest_size := hash.digest_size(&ctx)
tc.expect( testing.expectf(
t, t,
algo == api_algo, algo == api_algo,
fmt.tprintf( "%s/algorithm: Expected: %v but got %v instead",
"%s/algorithm: Expected: %v but got %v instead", algo_name,
algo_name, algo,
algo, api_algo,
api_algo,
),
) )
tc.expect( testing.expectf(
t, t,
hash.DIGEST_SIZES[algo] == api_digest_size, hash.DIGEST_SIZES[algo] == api_digest_size,
fmt.tprintf( "%s/digest_size: Expected: %d but got %d instead",
"%s/digest_size: Expected: %d but got %d instead", algo_name,
algo_name, hash.DIGEST_SIZES[algo],
hash.DIGEST_SIZES[algo], api_digest_size,
api_digest_size,
),
) )
hash.update(&ctx, digest_a) hash.update(&ctx, digest_a)
@@ -604,16 +586,14 @@ test_hash :: proc(t: ^testing.T) {
b_str = string(hex.encode(digest_b, context.temp_allocator)) b_str = string(hex.encode(digest_b, context.temp_allocator))
c_str := string(hex.encode(digest_c, context.temp_allocator)) c_str := string(hex.encode(digest_c, context.temp_allocator))
tc.expect( testing.expectf(
t, t,
a_str == b_str && b_str == c_str, a_str == b_str && b_str == c_str,
fmt.tprintf( "%s/rolling: Expected: %s (first) == %s (second) == %s (third)",
"%s/rolling: Expected: %s (first) == %s (second) == %s (third)", algo_name,
algo_name, a_str,
a_str, b_str,
b_str, c_str,
c_str,
),
) )
} }
} }
+18 -36
View File
@@ -2,28 +2,14 @@ package test_core_crypto
import "base:runtime" import "base:runtime"
import "core:encoding/hex" import "core:encoding/hex"
import "core:fmt"
import "core:testing" import "core:testing"
import "core:crypto/hash" import "core:crypto/hash"
import "core:crypto/hkdf" import "core:crypto/hkdf"
import "core:crypto/pbkdf2" import "core:crypto/pbkdf2"
import tc "tests:common"
@(test)
test_kdf :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
tc.log(t, "Testing KDFs")
test_hkdf(t)
test_pbkdf2(t)
}
@(test) @(test)
test_hkdf :: proc(t: ^testing.T) { test_hkdf :: proc(t: ^testing.T) {
tc.log(t, "Testing HKDF") runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
tmp: [128]byte // Good enough. tmp: [128]byte // Good enough.
@@ -70,25 +56,23 @@ test_hkdf :: proc(t: ^testing.T) {
dst_str := string(hex.encode(dst, context.temp_allocator)) dst_str := string(hex.encode(dst, context.temp_allocator))
tc.expect( testing.expectf(
t, t,
dst_str == v.okm, dst_str == v.okm,
fmt.tprintf( "HKDF-%s: Expected: %s for input of (%s, %s, %s), but got %s instead",
"HKDF-%s: Expected: %s for input of (%s, %s, %s), but got %s instead", algo_name,
algo_name, v.okm,
v.okm, v.ikm,
v.ikm, v.salt,
v.salt, v.info,
v.info, dst_str,
dst_str,
),
) )
} }
} }
@(test) @(test)
test_pbkdf2 :: proc(t: ^testing.T) { test_pbkdf2 :: proc(t: ^testing.T) {
tc.log(t, "Testing PBKDF2") runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
tmp: [64]byte // 512-bits is enough for every output for now. tmp: [64]byte // 512-bits is enough for every output for now.
@@ -174,18 +158,16 @@ test_pbkdf2 :: proc(t: ^testing.T) {
dst_str := string(hex.encode(dst, context.temp_allocator)) dst_str := string(hex.encode(dst, context.temp_allocator))
tc.expect( testing.expectf(
t, t,
dst_str == v.dk, dst_str == v.dk,
fmt.tprintf( "HMAC-%s: Expected: %s for input of (%s, %s, %d), but got %s instead",
"HMAC-%s: Expected: %s for input of (%s, %s, %d), but got %s instead", algo_name,
algo_name, v.dk,
v.dk, v.password,
v.password, v.salt,
v.salt, v.iterations,
v.iterations, dst_str,
dst_str,
),
) )
} }
} }
+30 -52
View File
@@ -2,30 +2,17 @@ package test_core_crypto
import "base:runtime" import "base:runtime"
import "core:encoding/hex" import "core:encoding/hex"
import "core:fmt"
import "core:mem" import "core:mem"
import "core:testing" import "core:testing"
import "core:crypto/hash" import "core:crypto/hash"
import "core:crypto/hmac" import "core:crypto/hmac"
import "core:crypto/poly1305" import "core:crypto/poly1305"
import "core:crypto/siphash" import "core:crypto/siphash"
import tc "tests:common"
@(test)
test_mac :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
tc.log(t, "Testing MACs")
test_hmac(t)
test_poly1305(t)
test_siphash_2_4(t)
}
@(test) @(test)
test_hmac :: proc(t: ^testing.T) { test_hmac :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
// Test cases pulled out of RFC 6234, note that HMAC is a generic // Test cases pulled out of RFC 6234, note that HMAC is a generic
// construct so as long as the underlying hash is correct and all // construct so as long as the underlying hash is correct and all
// the code paths are covered the implementation is "fine", so // the code paths are covered the implementation is "fine", so
@@ -86,40 +73,36 @@ test_hmac :: proc(t: ^testing.T) {
msg_str := string(hex.encode(msg, context.temp_allocator)) msg_str := string(hex.encode(msg, context.temp_allocator))
dst_str := string(hex.encode(dst[:tag_len], context.temp_allocator)) dst_str := string(hex.encode(dst[:tag_len], context.temp_allocator))
tc.expect( testing.expectf(
t, t,
dst_str == expected_str, dst_str == expected_str,
fmt.tprintf( "%s/incremental: Expected: %s for input of %s - %s, but got %s instead",
"%s/incremental: Expected: %s for input of %s - %s, but got %s instead", algo_name,
algo_name, tags_sha256[i],
tags_sha256[i], key_str,
key_str, msg_str,
msg_str, dst_str,
dst_str,
),
) )
hmac.sum(algo, dst, msg, key) hmac.sum(algo, dst, msg, key)
oneshot_str := string(hex.encode(dst[:tag_len], context.temp_allocator)) oneshot_str := string(hex.encode(dst[:tag_len], context.temp_allocator))
tc.expect( testing.expectf(
t, t,
oneshot_str == expected_str, oneshot_str == expected_str,
fmt.tprintf( "%s/oneshot: Expected: %s for input of %s - %s, but got %s instead",
"%s/oneshot: Expected: %s for input of %s - %s, but got %s instead", algo_name,
algo_name, tags_sha256[i],
tags_sha256[i], key_str,
key_str, msg_str,
msg_str, oneshot_str,
oneshot_str,
),
) )
} }
} }
@(test) @(test)
test_poly1305 :: proc(t: ^testing.T) { test_poly1305 :: proc(t: ^testing.T) {
tc.log(t, "Testing poly1305") runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
// Test cases taken from poly1305-donna. // Test cases taken from poly1305-donna.
key := [poly1305.KEY_SIZE]byte { key := [poly1305.KEY_SIZE]byte {
@@ -157,16 +140,17 @@ test_poly1305 :: proc(t: ^testing.T) {
// Verify - oneshot + compare // Verify - oneshot + compare
ok := poly1305.verify(tag[:], msg[:], key[:]) ok := poly1305.verify(tag[:], msg[:], key[:])
tc.expect(t, ok, "oneshot verify call failed") testing.expect(t, ok, "oneshot verify call failed")
// Sum - oneshot // Sum - oneshot
derived_tag: [poly1305.TAG_SIZE]byte derived_tag: [poly1305.TAG_SIZE]byte
poly1305.sum(derived_tag[:], msg[:], key[:]) poly1305.sum(derived_tag[:], msg[:], key[:])
derived_tag_str := string(hex.encode(derived_tag[:], context.temp_allocator)) derived_tag_str := string(hex.encode(derived_tag[:], context.temp_allocator))
tc.expect( testing.expectf(
t, t,
derived_tag_str == tag_str, derived_tag_str == tag_str,
fmt.tprintf("Expected %s for sum(msg, key), but got %s instead", tag_str, derived_tag_str), "Expected %s for sum(msg, key), but got %s instead",
tag_str, derived_tag_str,
) )
// Incremental // Incremental
@@ -182,21 +166,16 @@ test_poly1305 :: proc(t: ^testing.T) {
} }
poly1305.final(&ctx, derived_tag[:]) poly1305.final(&ctx, derived_tag[:])
derived_tag_str = string(hex.encode(derived_tag[:], context.temp_allocator)) derived_tag_str = string(hex.encode(derived_tag[:], context.temp_allocator))
tc.expect( testing.expectf(
t, t,
derived_tag_str == tag_str, derived_tag_str == tag_str,
fmt.tprintf( "Expected %s for init/update/final - incremental, but got %s instead",
"Expected %s for init/update/final - incremental, but got %s instead", tag_str, derived_tag_str,
tag_str,
derived_tag_str,
),
) )
} }
@(test) @(test)
test_siphash_2_4 :: proc(t: ^testing.T) { test_siphash_2_4 :: proc(t: ^testing.T) {
tc.log(t, "Testing SipHash-2-4")
// Test vectors from // Test vectors from
// https://github.com/veorq/SipHash/blob/master/vectors.h // https://github.com/veorq/SipHash/blob/master/vectors.h
test_vectors := [?]u64 { test_vectors := [?]u64 {
@@ -225,6 +204,7 @@ test_siphash_2_4 :: proc(t: ^testing.T) {
for i in 0 ..< len(test_vectors) { for i in 0 ..< len(test_vectors) {
data := make([]byte, i) data := make([]byte, i)
defer delete(data)
for j in 0 ..< i { for j in 0 ..< i {
data[j] = byte(j) data[j] = byte(j)
} }
@@ -232,15 +212,13 @@ test_siphash_2_4 :: proc(t: ^testing.T) {
vector := test_vectors[i] vector := test_vectors[i]
computed := siphash.sum_2_4(data[:], key[:]) computed := siphash.sum_2_4(data[:], key[:])
tc.expect( testing.expectf(
t, t,
computed == vector, computed == vector,
fmt.tprintf( "Expected: 0x%x for input of %v, but got 0x%x instead",
"Expected: 0x%x for input of %v, but got 0x%x instead", vector,
vector, data,
data, computed,
computed,
),
) )
} }
} }
@@ -2,30 +2,14 @@ package test_core_crypto
import "base:runtime" import "base:runtime"
import "core:encoding/hex" import "core:encoding/hex"
import "core:fmt"
import "core:testing" import "core:testing"
import "core:crypto/kmac" import "core:crypto/kmac"
import "core:crypto/shake" import "core:crypto/shake"
import "core:crypto/tuplehash" import "core:crypto/tuplehash"
import tc "tests:common"
@(test)
test_sha3_variants :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
tc.log(t, "Testing SHA3 derived functions")
test_shake(t)
test_cshake(t)
test_tuplehash(t)
test_kmac(t)
}
@(test) @(test)
test_shake :: proc(t: ^testing.T) { test_shake :: proc(t: ^testing.T) {
tc.log(t, "Testing SHAKE") runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
test_vectors := []struct { test_vectors := []struct {
sec_strength: int, sec_strength: int,
@@ -67,23 +51,21 @@ test_shake :: proc(t: ^testing.T) {
dst_str := string(hex.encode(dst, context.temp_allocator)) dst_str := string(hex.encode(dst, context.temp_allocator))
tc.expect( testing.expectf(
t, t,
dst_str == v.output, dst_str == v.output,
fmt.tprintf( "SHAKE%d: Expected: %s for input of %s, but got %s instead",
"SHAKE%d: Expected: %s for input of %s, but got %s instead", v.sec_strength,
v.sec_strength, v.output,
v.output, v.str,
v.str, dst_str,
dst_str,
),
) )
} }
} }
@(test) @(test)
test_cshake :: proc(t: ^testing.T) { test_cshake :: proc(t: ^testing.T) {
tc.log(t, "Testing cSHAKE") runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
test_vectors := []struct { test_vectors := []struct {
sec_strength: int, sec_strength: int,
@@ -135,29 +117,27 @@ test_cshake :: proc(t: ^testing.T) {
shake.init_cshake_256(&ctx, domainsep) shake.init_cshake_256(&ctx, domainsep)
} }
data, _ := hex.decode(transmute([]byte)(v.str)) data, _ := hex.decode(transmute([]byte)(v.str), context.temp_allocator)
shake.write(&ctx, data) shake.write(&ctx, data)
shake.read(&ctx, dst) shake.read(&ctx, dst)
dst_str := string(hex.encode(dst, context.temp_allocator)) dst_str := string(hex.encode(dst, context.temp_allocator))
tc.expect( testing.expectf(
t, t,
dst_str == v.output, dst_str == v.output,
fmt.tprintf( "cSHAKE%d: Expected: %s for input of %s, but got %s instead",
"cSHAKE%d: Expected: %s for input of %s, but got %s instead", v.sec_strength,
v.sec_strength, v.output,
v.output, v.str,
v.str, dst_str,
dst_str,
),
) )
} }
} }
@(test) @(test)
test_tuplehash :: proc(t: ^testing.T) { test_tuplehash :: proc(t: ^testing.T) {
tc.log(t, "Testing TupleHash(XOF)") runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
test_vectors := []struct { test_vectors := []struct {
sec_strength: int, sec_strength: int,
@@ -317,7 +297,7 @@ test_tuplehash :: proc(t: ^testing.T) {
} }
for e in v.tuple { for e in v.tuple {
data, _ := hex.decode(transmute([]byte)(e)) data, _ := hex.decode(transmute([]byte)(e), context.temp_allocator)
tuplehash.write_element(&ctx, data) tuplehash.write_element(&ctx, data)
} }
@@ -332,24 +312,22 @@ test_tuplehash :: proc(t: ^testing.T) {
dst_str := string(hex.encode(dst, context.temp_allocator)) dst_str := string(hex.encode(dst, context.temp_allocator))
tc.expect( testing.expectf(
t, t,
dst_str == v.output, dst_str == v.output,
fmt.tprintf( "TupleHash%s%d: Expected: %s for input of %v, but got %s instead",
"TupleHash%s%d: Expected: %s for input of %v, but got %s instead", suffix,
suffix, v.sec_strength,
v.sec_strength, v.output,
v.output, v.tuple,
v.tuple, dst_str,
dst_str,
),
) )
} }
} }
@(test) @(test)
test_kmac :: proc(t:^testing.T) { test_kmac :: proc(t:^testing.T) {
tc.log(t, "Testing KMAC") runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
test_vectors := []struct { test_vectors := []struct {
sec_strength: int, sec_strength: int,
@@ -410,7 +388,7 @@ test_kmac :: proc(t:^testing.T) {
for v in test_vectors { for v in test_vectors {
dst := make([]byte, len(v.output) / 2, context.temp_allocator) dst := make([]byte, len(v.output) / 2, context.temp_allocator)
key, _ := hex.decode(transmute([]byte)(v.key)) key, _ := hex.decode(transmute([]byte)(v.key), context.temp_allocator)
domainsep := transmute([]byte)(v.domainsep) domainsep := transmute([]byte)(v.domainsep)
ctx: kmac.Context ctx: kmac.Context
@@ -421,24 +399,22 @@ test_kmac :: proc(t:^testing.T) {
kmac.init_256(&ctx, key, domainsep) kmac.init_256(&ctx, key, domainsep)
} }
data, _ := hex.decode(transmute([]byte)(v.msg)) data, _ := hex.decode(transmute([]byte)(v.msg), context.temp_allocator)
kmac.update(&ctx, data) kmac.update(&ctx, data)
kmac.final(&ctx, dst) kmac.final(&ctx, dst)
dst_str := string(hex.encode(dst, context.temp_allocator)) dst_str := string(hex.encode(dst, context.temp_allocator))
tc.expect( testing.expectf(
t, t,
dst_str == v.output, dst_str == v.output,
fmt.tprintf( "KMAC%d: Expected: %s for input of (%s, %s, %s), but got %s instead",
"KMAC%d: Expected: %s for input of (%s, %s, %s), but got %s instead", v.sec_strength,
v.sec_strength, v.output,
v.output, v.key,
v.key, v.domainsep,
v.domainsep, v.msg,
v.msg, dst_str,
dst_str,
),
) )
} }
} }
@@ -1,361 +0,0 @@
package test_core_crypto
import "base:runtime"
import "core:encoding/hex"
import "core:fmt"
import "core:testing"
import "core:time"
import "core:crypto/aes"
import "core:crypto/chacha20"
import "core:crypto/chacha20poly1305"
import "core:crypto/ed25519"
import "core:crypto/poly1305"
import "core:crypto/x25519"
import tc "tests:common"
// Cryptographic primitive benchmarks.
@(test)
bench_crypto :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
fmt.println("Starting benchmarks:")
bench_chacha20(t)
bench_poly1305(t)
bench_chacha20poly1305(t)
bench_aes256_gcm(t)
bench_ed25519(t)
bench_x25519(t)
}
_setup_sized_buf :: proc(
options: ^time.Benchmark_Options,
allocator := context.allocator,
) -> (
err: time.Benchmark_Error,
) {
assert(options != nil)
options.input = make([]u8, options.bytes, allocator)
return nil if len(options.input) == options.bytes else .Allocation_Error
}
_teardown_sized_buf :: proc(
options: ^time.Benchmark_Options,
allocator := context.allocator,
) -> (
err: time.Benchmark_Error,
) {
assert(options != nil)
delete(options.input)
return nil
}
_benchmark_chacha20 :: proc(
options: ^time.Benchmark_Options,
allocator := context.allocator,
) -> (
err: time.Benchmark_Error,
) {
buf := options.input
key := [chacha20.KEY_SIZE]byte {
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
}
nonce := [chacha20.NONCE_SIZE]byte {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
}
ctx: chacha20.Context = ---
chacha20.init(&ctx, key[:], nonce[:])
for _ in 0 ..= options.rounds {
chacha20.xor_bytes(&ctx, buf, buf)
}
options.count = options.rounds
options.processed = options.rounds * options.bytes
return nil
}
_benchmark_poly1305 :: proc(
options: ^time.Benchmark_Options,
allocator := context.allocator,
) -> (
err: time.Benchmark_Error,
) {
buf := options.input
key := [poly1305.KEY_SIZE]byte {
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
}
tag: [poly1305.TAG_SIZE]byte = ---
for _ in 0 ..= options.rounds {
poly1305.sum(tag[:], buf, key[:])
}
options.count = options.rounds
options.processed = options.rounds * options.bytes
//options.hash = u128(h)
return nil
}
_benchmark_chacha20poly1305 :: proc(
options: ^time.Benchmark_Options,
allocator := context.allocator,
) -> (
err: time.Benchmark_Error,
) {
buf := options.input
key := [chacha20.KEY_SIZE]byte {
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
}
nonce := [chacha20.NONCE_SIZE]byte {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
}
tag: [chacha20poly1305.TAG_SIZE]byte = ---
for _ in 0 ..= options.rounds {
chacha20poly1305.encrypt(buf, tag[:], key[:], nonce[:], nil, buf)
}
options.count = options.rounds
options.processed = options.rounds * options.bytes
return nil
}
_benchmark_aes256_gcm :: proc(
options: ^time.Benchmark_Options,
allocator := context.allocator,
) -> (
err: time.Benchmark_Error,
) {
buf := options.input
nonce: [aes.GCM_NONCE_SIZE]byte
tag: [aes.GCM_TAG_SIZE]byte = ---
ctx := transmute(^aes.Context_GCM)context.user_ptr
for _ in 0 ..= options.rounds {
aes.seal_gcm(ctx, buf, tag[:], nonce[:], nil, buf)
}
options.count = options.rounds
options.processed = options.rounds * options.bytes
return nil
}
benchmark_print :: proc(name: string, options: ^time.Benchmark_Options) {
fmt.printf(
"\t[%v] %v rounds, %v bytes processed in %v ns\n\t\t%5.3f rounds/s, %5.3f MiB/s\n",
name,
options.rounds,
options.processed,
time.duration_nanoseconds(options.duration),
options.rounds_per_second,
options.megabytes_per_second,
)
}
bench_chacha20 :: proc(t: ^testing.T) {
name := "ChaCha20 64 bytes"
options := &time.Benchmark_Options {
rounds = 1_000,
bytes = 64,
setup = _setup_sized_buf,
bench = _benchmark_chacha20,
teardown = _teardown_sized_buf,
}
err := time.benchmark(options, context.allocator)
tc.expect(t, err == nil, name)
benchmark_print(name, options)
name = "ChaCha20 1024 bytes"
options.bytes = 1024
err = time.benchmark(options, context.allocator)
tc.expect(t, err == nil, name)
benchmark_print(name, options)
name = "ChaCha20 65536 bytes"
options.bytes = 65536
err = time.benchmark(options, context.allocator)
tc.expect(t, err == nil, name)
benchmark_print(name, options)
}
bench_poly1305 :: proc(t: ^testing.T) {
name := "Poly1305 64 zero bytes"
options := &time.Benchmark_Options {
rounds = 1_000,
bytes = 64,
setup = _setup_sized_buf,
bench = _benchmark_poly1305,
teardown = _teardown_sized_buf,
}
err := time.benchmark(options, context.allocator)
tc.expect(t, err == nil, name)
benchmark_print(name, options)
name = "Poly1305 1024 zero bytes"
options.bytes = 1024
err = time.benchmark(options, context.allocator)
tc.expect(t, err == nil, name)
benchmark_print(name, options)
}
bench_chacha20poly1305 :: proc(t: ^testing.T) {
name := "chacha20poly1305 64 bytes"
options := &time.Benchmark_Options {
rounds = 1_000,
bytes = 64,
setup = _setup_sized_buf,
bench = _benchmark_chacha20poly1305,
teardown = _teardown_sized_buf,
}
err := time.benchmark(options, context.allocator)
tc.expect(t, err == nil, name)
benchmark_print(name, options)
name = "chacha20poly1305 1024 bytes"
options.bytes = 1024
err = time.benchmark(options, context.allocator)
tc.expect(t, err == nil, name)
benchmark_print(name, options)
name = "chacha20poly1305 65536 bytes"
options.bytes = 65536
err = time.benchmark(options, context.allocator)
tc.expect(t, err == nil, name)
benchmark_print(name, options)
}
bench_aes256_gcm :: proc(t: ^testing.T) {
name := "AES256-GCM 64 bytes"
options := &time.Benchmark_Options {
rounds = 1_000,
bytes = 64,
setup = _setup_sized_buf,
bench = _benchmark_aes256_gcm,
teardown = _teardown_sized_buf,
}
key := [aes.KEY_SIZE_256]byte {
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
}
ctx: aes.Context_GCM
aes.init_gcm(&ctx, key[:])
context.user_ptr = &ctx
err := time.benchmark(options, context.allocator)
tc.expect(t, err == nil, name)
benchmark_print(name, options)
name = "AES256-GCM 1024 bytes"
options.bytes = 1024
err = time.benchmark(options, context.allocator)
tc.expect(t, err == nil, name)
benchmark_print(name, options)
name = "AES256-GCM 65536 bytes"
options.bytes = 65536
err = time.benchmark(options, context.allocator)
tc.expect(t, err == nil, name)
benchmark_print(name, options)
}
bench_ed25519 :: proc(t: ^testing.T) {
iters :: 10000
priv_str := "cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"
priv_bytes, _ := hex.decode(transmute([]byte)(priv_str), context.temp_allocator)
priv_key: ed25519.Private_Key
start := time.now()
for i := 0; i < iters; i = i + 1 {
ok := ed25519.private_key_set_bytes(&priv_key, priv_bytes)
assert(ok, "private key should deserialize")
}
elapsed := time.since(start)
tc.log(
t,
fmt.tprintf(
"ed25519.private_key_set_bytes: ~%f us/op",
time.duration_microseconds(elapsed) / iters,
),
)
pub_bytes := priv_key._pub_key._b[:] // "I know what I am doing"
pub_key: ed25519.Public_Key
start = time.now()
for i := 0; i < iters; i = i + 1 {
ok := ed25519.public_key_set_bytes(&pub_key, pub_bytes[:])
assert(ok, "public key should deserialize")
}
elapsed = time.since(start)
tc.log(
t,
fmt.tprintf(
"ed25519.public_key_set_bytes: ~%f us/op",
time.duration_microseconds(elapsed) / iters,
),
)
msg := "Got a job for you, 621."
sig_bytes: [ed25519.SIGNATURE_SIZE]byte
msg_bytes := transmute([]byte)(msg)
start = time.now()
for i := 0; i < iters; i = i + 1 {
ed25519.sign(&priv_key, msg_bytes, sig_bytes[:])
}
elapsed = time.since(start)
tc.log(t, fmt.tprintf("ed25519.sign: ~%f us/op", time.duration_microseconds(elapsed) / iters))
start = time.now()
for i := 0; i < iters; i = i + 1 {
ok := ed25519.verify(&pub_key, msg_bytes, sig_bytes[:])
assert(ok, "signature should validate")
}
elapsed = time.since(start)
tc.log(
t,
fmt.tprintf("ed25519.verify: ~%f us/op", time.duration_microseconds(elapsed) / iters),
)
}
bench_x25519 :: proc(t: ^testing.T) {
point_str := "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
scalar_str := "cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"
point, _ := hex.decode(transmute([]byte)(point_str), context.temp_allocator)
scalar, _ := hex.decode(transmute([]byte)(scalar_str), context.temp_allocator)
out: [x25519.POINT_SIZE]byte = ---
iters :: 10000
start := time.now()
for i := 0; i < iters; i = i + 1 {
x25519.scalarmult(out[:], scalar[:], point[:])
}
elapsed := time.since(start)
tc.log(
t,
fmt.tprintf("x25519.scalarmult: ~%f us/op", time.duration_microseconds(elapsed) / iters),
)
}
+21 -44
View File
@@ -1,61 +1,38 @@
package test_encoding_base64 package test_encoding_base64
import "base:intrinsics" import "base:intrinsics"
import "core:encoding/base64" import "core:encoding/base64"
import "core:fmt"
import "core:os"
import "core:reflect"
import "core:testing" import "core:testing"
TEST_count := 0 Test :: struct {
TEST_fail := 0 vector: string,
base64: string,
when ODIN_TEST {
expect_value :: testing.expect_value
} else {
expect_value :: proc(t: ^testing.T, value, expected: $T, loc := #caller_location) -> bool where intrinsics.type_is_comparable(T) {
TEST_count += 1
ok := value == expected || reflect.is_nil(value) && reflect.is_nil(expected)
if !ok {
TEST_fail += 1
fmt.printf("[%v] expected %v, got %v\n", loc, expected, value)
}
return ok
}
} }
main :: proc() { tests :: []Test{
t := testing.T{} {"", ""},
{"f", "Zg=="},
test_encoding(&t) {"fo", "Zm8="},
test_decoding(&t) {"foo", "Zm9v"},
{"foob", "Zm9vYg=="},
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) {"fooba", "Zm9vYmE="},
if TEST_fail > 0 { {"foobar", "Zm9vYmFy"},
os.exit(1)
}
} }
@(test) @(test)
test_encoding :: proc(t: ^testing.T) { test_encoding :: proc(t: ^testing.T) {
expect_value(t, base64.encode(transmute([]byte)string("")), "") for test in tests {
expect_value(t, base64.encode(transmute([]byte)string("f")), "Zg==") v := base64.encode(transmute([]byte)test.vector)
expect_value(t, base64.encode(transmute([]byte)string("fo")), "Zm8=") defer delete(v)
expect_value(t, base64.encode(transmute([]byte)string("foo")), "Zm9v") testing.expect_value(t, v, test.base64)
expect_value(t, base64.encode(transmute([]byte)string("foob")), "Zm9vYg==") }
expect_value(t, base64.encode(transmute([]byte)string("fooba")), "Zm9vYmE=")
expect_value(t, base64.encode(transmute([]byte)string("foobar")), "Zm9vYmFy")
} }
@(test) @(test)
test_decoding :: proc(t: ^testing.T) { test_decoding :: proc(t: ^testing.T) {
expect_value(t, string(base64.decode("")), "") for test in tests {
expect_value(t, string(base64.decode("Zg==")), "f") v := string(base64.decode(test.base64))
expect_value(t, string(base64.decode("Zm8=")), "fo") defer delete(v)
expect_value(t, string(base64.decode("Zm9v")), "foo") testing.expect_value(t, v, test.vector)
expect_value(t, string(base64.decode("Zm9vYg==")), "foob") }
expect_value(t, string(base64.decode("Zm9vYmE=")), "fooba")
expect_value(t, string(base64.decode("Zm9vYmFy")), "foobar")
} }
+132 -209
View File
@@ -1,105 +1,15 @@
package test_encoding_cbor package test_encoding_cbor
import "base:intrinsics" import "base:intrinsics"
import "core:bytes" import "core:bytes"
import "core:encoding/cbor" import "core:encoding/cbor"
import "core:fmt" import "core:fmt"
import "core:io" import "core:io"
import "core:math/big" import "core:math/big"
import "core:mem"
import "core:os"
import "core:reflect" import "core:reflect"
import "core:testing" import "core:testing"
import "core:time" import "core:time"
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
expect_value :: testing.expect_value
errorf :: testing.errorf
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
expect_value :: proc(t: ^testing.T, value, expected: $T, loc := #caller_location) -> bool where intrinsics.type_is_comparable(T) {
TEST_count += 1
ok := value == expected || reflect.is_nil(value) && reflect.is_nil(expected)
if !ok {
TEST_fail += 1
fmt.printf("[%v] expected %v, got %v\n", loc, expected, value)
}
return ok
}
errorf :: proc(t: ^testing.T, fmts: string, args: ..any, loc := #caller_location) {
TEST_fail += 1
fmt.printf("[%v] ERROR: ", loc)
fmt.printf(fmts, ..args)
fmt.println()
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
main :: proc() {
t := testing.T{}
test_marshalling(&t)
test_marshalling_maybe(&t)
test_marshalling_nil_maybe(&t)
test_marshalling_union(&t)
test_lying_length_array(&t)
test_decode_unsigned(&t)
test_encode_unsigned(&t)
test_decode_negative(&t)
test_encode_negative(&t)
test_decode_simples(&t)
test_encode_simples(&t)
test_decode_floats(&t)
test_encode_floats(&t)
test_decode_bytes(&t)
test_encode_bytes(&t)
test_decode_strings(&t)
test_encode_strings(&t)
test_decode_lists(&t)
test_encode_lists(&t)
test_decode_maps(&t)
test_encode_maps(&t)
test_decode_tags(&t)
test_encode_tags(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
Foo :: struct { Foo :: struct {
str: string, str: string,
cstr: cstring, cstr: cstring,
@@ -143,14 +53,6 @@ FooBars :: bit_set[FooBar; u16]
@(test) @(test)
test_marshalling :: proc(t: ^testing.T) { test_marshalling :: proc(t: ^testing.T) {
tracker: mem.Tracking_Allocator
mem.tracking_allocator_init(&tracker, context.allocator)
context.allocator = mem.tracking_allocator(&tracker)
context.temp_allocator = context.allocator
defer mem.tracking_allocator_destroy(&tracker)
ev :: expect_value
{ {
nice := "16 is a nice number" nice := "16 is a nice number"
now := time.Time{_nsec = 1701117968 * 1e9} now := time.Time{_nsec = 1701117968 * 1e9}
@@ -205,18 +107,18 @@ test_marshalling :: proc(t: ^testing.T) {
} }
data, err := cbor.marshal(f, cbor.ENCODE_FULLY_DETERMINISTIC) data, err := cbor.marshal(f, cbor.ENCODE_FULLY_DETERMINISTIC)
ev(t, err, nil) testing.expect_value(t, err, nil)
defer delete(data) defer delete(data)
decoded, derr := cbor.decode(string(data)) decoded, derr := cbor.decode(string(data))
ev(t, derr, nil) testing.expect_value(t, derr, nil)
defer cbor.destroy(decoded) defer cbor.destroy(decoded)
diagnosis, eerr := cbor.to_diagnostic_format(decoded) diagnosis, eerr := cbor.to_diagnostic_format(decoded)
ev(t, eerr, nil) testing.expect_value(t, eerr, nil)
defer delete(diagnosis) defer delete(diagnosis)
ev(t, diagnosis, `{ testing.expect_value(t, diagnosis, `{
"base64": 34("MTYgaXMgYSBuaWNlIG51bWJlcg=="), "base64": 34("MTYgaXMgYSBuaWNlIG51bWJlcg=="),
"biggest": 2(h'f951a9fd3c158afdff08ab8e0'), "biggest": 2(h'f951a9fd3c158afdff08ab8e0'),
"biggie": 18446744073709551615, "biggie": 18446744073709551615,
@@ -285,7 +187,7 @@ test_marshalling :: proc(t: ^testing.T) {
backf: Foo backf: Foo
uerr := cbor.unmarshal(string(data), &backf) uerr := cbor.unmarshal(string(data), &backf)
ev(t, uerr, nil) testing.expect_value(t, uerr, nil)
defer { defer {
delete(backf.str) delete(backf.str)
delete(backf.cstr) delete(backf.cstr)
@@ -304,104 +206,102 @@ test_marshalling :: proc(t: ^testing.T) {
big.destroy(&backf.smallest) big.destroy(&backf.smallest)
} }
ev(t, backf.str, f.str) testing.expect_value(t, backf.str, f.str)
ev(t, backf.cstr, f.cstr) testing.expect_value(t, backf.cstr, f.cstr)
#partial switch v in backf.value { #partial switch v in backf.value {
case ^cbor.Map: case ^cbor.Map:
for entry, i in v { for entry, i in v {
fm := f.value.(^cbor.Map) fm := f.value.(^cbor.Map)
ev(t, entry.key, fm[i].key) testing.expect_value(t, entry.key, fm[i].key)
if str, is_str := entry.value.(^cbor.Text); is_str { if str, is_str := entry.value.(^cbor.Text); is_str {
ev(t, str^, fm[i].value.(^cbor.Text)^) testing.expect_value(t, str^, fm[i].value.(^cbor.Text)^)
} else { } else {
ev(t, entry.value, fm[i].value) testing.expect_value(t, entry.value, fm[i].value)
} }
} }
case: errorf(t, "wrong type %v", v) case: testing.expectf(t, false, "wrong type %v", v)
} }
ev(t, backf.neg, f.neg) testing.expect_value(t, backf.neg, f.neg)
ev(t, backf.iamint, f.iamint) testing.expect_value(t, backf.iamint, f.iamint)
ev(t, backf.base64, f.base64) testing.expect_value(t, backf.base64, f.base64)
ev(t, backf.renamed, f.renamed) testing.expect_value(t, backf.renamed, f.renamed)
ev(t, backf.now, f.now) testing.expect_value(t, backf.now, f.now)
ev(t, backf.nowie, f.nowie) testing.expect_value(t, backf.nowie, f.nowie)
for e, i in f.child.dyn { ev(t, backf.child.dyn[i], e) } for e, i in f.child.dyn { testing.expect_value(t, backf.child.dyn[i], e) }
for key, value in f.child.mappy { ev(t, backf.child.mappy[key], value) } for key, value in f.child.mappy { testing.expect_value(t, backf.child.mappy[key], value) }
ev(t, backf.child.my_integers, f.child.my_integers) testing.expect_value(t, backf.child.my_integers, f.child.my_integers)
ev(t, len(backf.my_bytes), 0) testing.expect_value(t, len(backf.my_bytes), 0)
ev(t, len(backf.my_bytes), len(f.my_bytes)) testing.expect_value(t, len(backf.my_bytes), len(f.my_bytes))
ev(t, backf.ennie, f.ennie) testing.expect_value(t, backf.ennie, f.ennie)
ev(t, backf.ennieb, f.ennieb) testing.expect_value(t, backf.ennieb, f.ennieb)
ev(t, backf.quat, f.quat) testing.expect_value(t, backf.quat, f.quat)
ev(t, backf.comp, f.comp) testing.expect_value(t, backf.comp, f.comp)
ev(t, backf.important, f.important) testing.expect_value(t, backf.important, f.important)
ev(t, backf.no, nil) testing.expect_value(t, backf.no, nil)
ev(t, backf.nos, nil) testing.expect_value(t, backf.nos, nil)
ev(t, backf.yes, f.yes) testing.expect_value(t, backf.yes, f.yes)
ev(t, backf.biggie, f.biggie) testing.expect_value(t, backf.biggie, f.biggie)
ev(t, backf.smallie, f.smallie) testing.expect_value(t, backf.smallie, f.smallie)
ev(t, backf.onetwenty, f.onetwenty) testing.expect_value(t, backf.onetwenty, f.onetwenty)
ev(t, backf.small_onetwenty, f.small_onetwenty) testing.expect_value(t, backf.small_onetwenty, f.small_onetwenty)
ev(t, backf.ignore_this, nil) testing.expect_value(t, backf.ignore_this, nil)
s_equals, s_err := big.equals(&backf.smallest, &f.smallest) s_equals, s_err := big.equals(&backf.smallest, &f.smallest)
ev(t, s_err, nil) testing.expect_value(t, s_err, nil)
if !s_equals { if !s_equals {
errorf(t, "smallest: %v does not equal %v", big.itoa(&backf.smallest), big.itoa(&f.smallest)) testing.expectf(t, false, "smallest: %v does not equal %v", big.itoa(&backf.smallest), big.itoa(&f.smallest))
} }
b_equals, b_err := big.equals(&backf.biggest, &f.biggest) b_equals, b_err := big.equals(&backf.biggest, &f.biggest)
ev(t, b_err, nil) testing.expect_value(t, b_err, nil)
if !b_equals { if !b_equals {
errorf(t, "biggest: %v does not equal %v", big.itoa(&backf.biggest), big.itoa(&f.biggest)) testing.expectf(t, false, "biggest: %v does not equal %v", big.itoa(&backf.biggest), big.itoa(&f.biggest))
} }
} }
for _, leak in tracker.allocation_map {
errorf(t, "%v leaked %m\n", leak.location, leak.size)
}
for bad_free in tracker.bad_free_array {
errorf(t, "%v allocation %p was freed badly\n", bad_free.location, bad_free.memory)
}
} }
@(test) @(test)
test_marshalling_maybe :: proc(t: ^testing.T) { test_marshalling_maybe :: proc(t: ^testing.T) {
maybe_test: Maybe(int) = 1 maybe_test: Maybe(int) = 1
data, err := cbor.marshal(maybe_test) data, err := cbor.marshal(maybe_test)
expect_value(t, err, nil) defer delete(data)
testing.expect_value(t, err, nil)
val, derr := cbor.decode(string(data)) val, derr := cbor.decode(string(data))
expect_value(t, derr, nil) testing.expect_value(t, derr, nil)
expect_value(t, cbor.to_diagnostic_format(val), "1") diag := cbor.to_diagnostic_format(val)
testing.expect_value(t, diag, "1")
delete(diag)
maybe_dest: Maybe(int) maybe_dest: Maybe(int)
uerr := cbor.unmarshal(string(data), &maybe_dest) uerr := cbor.unmarshal(string(data), &maybe_dest)
expect_value(t, uerr, nil) testing.expect_value(t, uerr, nil)
expect_value(t, maybe_dest, 1) testing.expect_value(t, maybe_dest, 1)
} }
@(test) @(test)
test_marshalling_nil_maybe :: proc(t: ^testing.T) { test_marshalling_nil_maybe :: proc(t: ^testing.T) {
maybe_test: Maybe(int) maybe_test: Maybe(int)
data, err := cbor.marshal(maybe_test) data, err := cbor.marshal(maybe_test)
expect_value(t, err, nil) defer delete(data)
testing.expect_value(t, err, nil)
val, derr := cbor.decode(string(data)) val, derr := cbor.decode(string(data))
expect_value(t, derr, nil) testing.expect_value(t, derr, nil)
expect_value(t, cbor.to_diagnostic_format(val), "nil") diag := cbor.to_diagnostic_format(val)
testing.expect_value(t, diag, "nil")
delete(diag)
maybe_dest: Maybe(int) maybe_dest: Maybe(int)
uerr := cbor.unmarshal(string(data), &maybe_dest) uerr := cbor.unmarshal(string(data), &maybe_dest)
expect_value(t, uerr, nil) testing.expect_value(t, uerr, nil)
expect_value(t, maybe_dest, nil) testing.expect_value(t, maybe_dest, nil)
} }
@(test) @(test)
@@ -427,17 +327,24 @@ test_marshalling_union :: proc(t: ^testing.T) {
{ {
test: My_Union = My_Distinct("Hello, World!") test: My_Union = My_Distinct("Hello, World!")
data, err := cbor.marshal(test) data, err := cbor.marshal(test)
expect_value(t, err, nil) defer delete(data)
testing.expect_value(t, err, nil)
val, derr := cbor.decode(string(data)) val, derr := cbor.decode(string(data))
expect_value(t, derr, nil) defer cbor.destroy(val)
testing.expect_value(t, derr, nil)
expect_value(t, cbor.to_diagnostic_format(val, -1), `1010(["My_Distinct", "Hello, World!"])`) diag := cbor.to_diagnostic_format(val, -1)
defer delete(diag)
testing.expect_value(t, diag, `1010(["My_Distinct", "Hello, World!"])`)
dest: My_Union dest: My_Union
uerr := cbor.unmarshal(string(data), &dest) uerr := cbor.unmarshal(string(data), &dest)
expect_value(t, uerr, nil) testing.expect_value(t, uerr, nil)
expect_value(t, dest, My_Distinct("Hello, World!")) testing.expect_value(t, dest, My_Distinct("Hello, World!"))
if str, ok := dest.(My_Distinct); ok {
delete(string(str))
}
} }
My_Union_No_Nil :: union #no_nil { My_Union_No_Nil :: union #no_nil {
@@ -450,17 +357,21 @@ test_marshalling_union :: proc(t: ^testing.T) {
{ {
test: My_Union_No_Nil = My_Struct{.Two} test: My_Union_No_Nil = My_Struct{.Two}
data, err := cbor.marshal(test) data, err := cbor.marshal(test)
expect_value(t, err, nil) defer delete(data)
testing.expect_value(t, err, nil)
val, derr := cbor.decode(string(data)) val, derr := cbor.decode(string(data))
expect_value(t, derr, nil) defer cbor.destroy(val)
testing.expect_value(t, derr, nil)
expect_value(t, cbor.to_diagnostic_format(val, -1), `1010(["My_Struct", {"my_enum": 1}])`) diag := cbor.to_diagnostic_format(val, -1)
defer delete(diag)
testing.expect_value(t, diag, `1010(["My_Struct", {"my_enum": 1}])`)
dest: My_Union_No_Nil dest: My_Union_No_Nil
uerr := cbor.unmarshal(string(data), &dest) uerr := cbor.unmarshal(string(data), &dest)
expect_value(t, uerr, nil) testing.expect_value(t, uerr, nil)
expect_value(t, dest, My_Struct{.Two}) testing.expect_value(t, dest, My_Struct{.Two})
} }
} }
@@ -469,7 +380,7 @@ test_lying_length_array :: proc(t: ^testing.T) {
// Input says this is an array of length max(u64), this should not allocate that amount. // Input says this is an array of length max(u64), this should not allocate that amount.
input := []byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42} input := []byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}
_, err := cbor.decode(string(input)) _, err := cbor.decode(string(input))
expect_value(t, err, io.Error.Unexpected_EOF) // .Out_Of_Memory would be bad. testing.expect_value(t, err, io.Error.Unexpected_EOF) // .Out_Of_Memory would be bad.
} }
@(test) @(test)
@@ -691,65 +602,73 @@ test_encode_lists :: proc(t: ^testing.T) {
expect_streamed_encoding(t, "\x9f\xff", &cbor.Array{}) expect_streamed_encoding(t, "\x9f\xff", &cbor.Array{})
{ {
bytes.buffer_reset(&buf) buf: bytes.Buffer
bytes.buffer_init_allocator(&buf, 0, 0)
defer bytes.buffer_destroy(&buf)
stream := bytes.buffer_to_stream(&buf)
encoder := cbor.Encoder{cbor.ENCODE_FULLY_DETERMINISTIC, stream, {}}
err: cbor.Encode_Error err: cbor.Encode_Error
err = cbor.encode_stream_begin(stream, .Array) err = cbor.encode_stream_begin(stream, .Array)
expect_value(t, err, nil) testing.expect_value(t, err, nil)
{ {
err = cbor.encode_stream_array_item(encoder, u8(1)) err = cbor.encode_stream_array_item(encoder, u8(1))
expect_value(t, err, nil) testing.expect_value(t, err, nil)
err = cbor.encode_stream_array_item(encoder, &cbor.Array{u8(2), u8(3)}) err = cbor.encode_stream_array_item(encoder, &cbor.Array{u8(2), u8(3)})
expect_value(t, err, nil) testing.expect_value(t, err, nil)
err = cbor.encode_stream_begin(stream, .Array) err = cbor.encode_stream_begin(stream, .Array)
expect_value(t, err, nil) testing.expect_value(t, err, nil)
{ {
err = cbor.encode_stream_array_item(encoder, u8(4)) err = cbor.encode_stream_array_item(encoder, u8(4))
expect_value(t, err, nil) testing.expect_value(t, err, nil)
err = cbor.encode_stream_array_item(encoder, u8(5)) err = cbor.encode_stream_array_item(encoder, u8(5))
expect_value(t, err, nil) testing.expect_value(t, err, nil)
} }
err = cbor.encode_stream_end(stream) err = cbor.encode_stream_end(stream)
expect_value(t, err, nil) testing.expect_value(t, err, nil)
} }
err = cbor.encode_stream_end(stream) err = cbor.encode_stream_end(stream)
expect_value(t, err, nil) testing.expect_value(t, err, nil)
expect_value(t, fmt.tprint(bytes.buffer_to_bytes(&buf)), fmt.tprint(transmute([]byte)string("\x9f\x01\x82\x02\x03\x9f\x04\x05\xff\xff"))) testing.expect_value(t, fmt.tprint(bytes.buffer_to_bytes(&buf)), fmt.tprint(transmute([]byte)string("\x9f\x01\x82\x02\x03\x9f\x04\x05\xff\xff")))
} }
{ {
bytes.buffer_reset(&buf) buf: bytes.Buffer
bytes.buffer_init_allocator(&buf, 0, 0)
defer bytes.buffer_destroy(&buf)
stream := bytes.buffer_to_stream(&buf)
encoder := cbor.Encoder{cbor.ENCODE_FULLY_DETERMINISTIC, stream, {}}
err: cbor.Encode_Error err: cbor.Encode_Error
err = cbor._encode_u8(stream, 2, .Array) err = cbor._encode_u8(stream, 2, .Array)
expect_value(t, err, nil) testing.expect_value(t, err, nil)
a := "a" a := "a"
err = cbor.encode(encoder, &a) err = cbor.encode(encoder, &a)
expect_value(t, err, nil) testing.expect_value(t, err, nil)
{ {
err = cbor.encode_stream_begin(stream, .Map) err = cbor.encode_stream_begin(stream, .Map)
expect_value(t, err, nil) testing.expect_value(t, err, nil)
b := "b" b := "b"
c := "c" c := "c"
err = cbor.encode_stream_map_entry(encoder, &b, &c) err = cbor.encode_stream_map_entry(encoder, &b, &c)
expect_value(t, err, nil) testing.expect_value(t, err, nil)
err = cbor.encode_stream_end(stream) err = cbor.encode_stream_end(stream)
expect_value(t, err, nil) testing.expect_value(t, err, nil)
} }
expect_value(t, fmt.tprint(bytes.buffer_to_bytes(&buf)), fmt.tprint(transmute([]byte)string("\x82\x61\x61\xbf\x61\x62\x61\x63\xff"))) testing.expect_value(t, fmt.tprint(bytes.buffer_to_bytes(&buf)), fmt.tprint(transmute([]byte)string("\x82\x61\x61\xbf\x61\x62\x61\x63\xff")))
} }
} }
@@ -807,30 +726,30 @@ expect_decoding :: proc(t: ^testing.T, encoded: string, decoded: string, type: t
res, err := cbor.decode(encoded) res, err := cbor.decode(encoded)
defer cbor.destroy(res) defer cbor.destroy(res)
expect_value(t, reflect.union_variant_typeid(res), type, loc) testing.expect_value(t, reflect.union_variant_typeid(res), type, loc)
expect_value(t, err, nil, loc) testing.expect_value(t, err, nil, loc)
str := cbor.to_diagnostic_format(res, padding=-1) str := cbor.to_diagnostic_format(res, padding=-1)
defer delete(str) defer delete(str)
expect_value(t, str, decoded, loc) testing.expect_value(t, str, decoded, loc)
} }
expect_tag :: proc(t: ^testing.T, encoded: string, nr: cbor.Tag_Number, value_decoded: string, loc := #caller_location) { expect_tag :: proc(t: ^testing.T, encoded: string, nr: cbor.Tag_Number, value_decoded: string, loc := #caller_location) {
res, err := cbor.decode(encoded) res, err := cbor.decode(encoded)
defer cbor.destroy(res) defer cbor.destroy(res)
expect_value(t, err, nil, loc) testing.expect_value(t, err, nil, loc)
if tag, is_tag := res.(^cbor.Tag); is_tag { if tag, is_tag := res.(^cbor.Tag); is_tag {
expect_value(t, tag.number, nr, loc) testing.expect_value(t, tag.number, nr, loc)
str := cbor.to_diagnostic_format(tag, padding=-1) str := cbor.to_diagnostic_format(tag, padding=-1)
defer delete(str) defer delete(str)
expect_value(t, str, value_decoded, loc) testing.expect_value(t, str, value_decoded, loc)
} else { } else {
errorf(t, "Value %#v is not a tag", res, loc) testing.expectf(t, false, "Value %#v is not a tag", res, loc)
} }
} }
@@ -838,35 +757,39 @@ expect_float :: proc(t: ^testing.T, encoded: string, expected: $T, loc := #calle
res, err := cbor.decode(encoded) res, err := cbor.decode(encoded)
defer cbor.destroy(res) defer cbor.destroy(res)
expect_value(t, reflect.union_variant_typeid(res), typeid_of(T), loc) testing.expect_value(t, reflect.union_variant_typeid(res), typeid_of(T), loc)
expect_value(t, err, nil, loc) testing.expect_value(t, err, nil, loc)
#partial switch r in res { #partial switch r in res {
case f16: case f16:
when T == f16 { expect_value(t, res, expected, loc) } else { unreachable() } when T == f16 { testing.expect_value(t, res, expected, loc) } else { unreachable() }
case f32: case f32:
when T == f32 { expect_value(t, res, expected, loc) } else { unreachable() } when T == f32 { testing.expect_value(t, res, expected, loc) } else { unreachable() }
case f64: case f64:
when T == f64 { expect_value(t, res, expected, loc) } else { unreachable() } when T == f64 { testing.expect_value(t, res, expected, loc) } else { unreachable() }
case: case:
unreachable() unreachable()
} }
} }
buf: bytes.Buffer
stream := bytes.buffer_to_stream(&buf)
encoder := cbor.Encoder{cbor.ENCODE_FULLY_DETERMINISTIC, stream, {}}
expect_encoding :: proc(t: ^testing.T, val: cbor.Value, encoded: string, loc := #caller_location) { expect_encoding :: proc(t: ^testing.T, val: cbor.Value, encoded: string, loc := #caller_location) {
bytes.buffer_reset(&buf) buf: bytes.Buffer
bytes.buffer_init_allocator(&buf, 0, 0)
defer bytes.buffer_destroy(&buf)
stream := bytes.buffer_to_stream(&buf)
encoder := cbor.Encoder{cbor.ENCODE_FULLY_DETERMINISTIC, stream, {}}
err := cbor.encode(encoder, val) err := cbor.encode(encoder, val, loc)
expect_value(t, err, nil, loc) testing.expect_value(t, err, nil, loc)
expect_value(t, fmt.tprint(bytes.buffer_to_bytes(&buf)), fmt.tprint(transmute([]byte)encoded), loc) testing.expect_value(t, fmt.tprint(bytes.buffer_to_bytes(&buf)), fmt.tprint(transmute([]byte)encoded), loc)
} }
expect_streamed_encoding :: proc(t: ^testing.T, encoded: string, values: ..cbor.Value, loc := #caller_location) { expect_streamed_encoding :: proc(t: ^testing.T, encoded: string, values: ..cbor.Value, loc := #caller_location) {
bytes.buffer_reset(&buf) buf: bytes.Buffer
bytes.buffer_init_allocator(&buf, 0, 0)
defer bytes.buffer_destroy(&buf)
stream := bytes.buffer_to_stream(&buf)
encoder := cbor.Encoder{cbor.ENCODE_FULLY_DETERMINISTIC, stream, {}}
for value, i in values { for value, i in values {
err: cbor.Encode_Error err: cbor.Encode_Error
@@ -891,15 +814,15 @@ expect_streamed_encoding :: proc(t: ^testing.T, encoded: string, values: ..cbor.
if err2 != nil { break } if err2 != nil { break }
} }
case: case:
errorf(t, "%v does not support streamed encoding", reflect.union_variant_typeid(value)) testing.expectf(t, false, "%v does not support streamed encoding", reflect.union_variant_typeid(value))
} }
expect_value(t, err, nil, loc) testing.expect_value(t, err, nil, loc)
expect_value(t, err2, nil, loc) testing.expect_value(t, err2, nil, loc)
} }
err := cbor.encode_stream_end(stream) err := cbor.encode_stream_end(stream)
expect_value(t, err, nil, loc) testing.expect_value(t, err, nil, loc)
expect_value(t, fmt.tprint(bytes.buffer_to_bytes(&buf)), fmt.tprint(transmute([]byte)encoded), loc) testing.expect_value(t, fmt.tprint(bytes.buffer_to_bytes(&buf)), fmt.tprint(transmute([]byte)encoded), loc)
} }
+44 -50
View File
@@ -2,42 +2,6 @@ package test_core_hex
import "core:encoding/hex" import "core:encoding/hex"
import "core:testing" import "core:testing"
import "core:fmt"
import "core:os"
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
main :: proc() {
t := testing.T{}
hex_encode(&t)
hex_decode(&t)
hex_decode_sequence(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
CASES :: [][2]string{ CASES :: [][2]string{
{"11", "3131"}, {"11", "3131"},
@@ -49,10 +13,14 @@ CASES :: [][2]string{
hex_encode :: proc(t: ^testing.T) { hex_encode :: proc(t: ^testing.T) {
for test in CASES { for test in CASES {
encoded := string(hex.encode(transmute([]byte)test[0])) encoded := string(hex.encode(transmute([]byte)test[0]))
expect( defer delete(encoded)
testing.expectf(
t, t,
encoded == test[1], encoded == test[1],
fmt.tprintf("encode: %q -> %q (should be: %q)", test[0], encoded, test[1]), "encode: %q -> %q (should be: %q)",
test[0],
encoded,
test[1],
) )
} }
} }
@@ -61,11 +29,20 @@ hex_encode :: proc(t: ^testing.T) {
hex_decode :: proc(t: ^testing.T) { hex_decode :: proc(t: ^testing.T) {
for test in CASES { for test in CASES {
decoded, ok := hex.decode(transmute([]byte)test[1]) decoded, ok := hex.decode(transmute([]byte)test[1])
expect(t, ok, fmt.tprintf("decode: %q not ok", test[1])) defer delete(decoded)
expect( testing.expectf(
t,
ok,
"decode: %q not ok",
test[1],
)
testing.expectf(
t, t,
string(decoded) == test[0], string(decoded) == test[0],
fmt.tprintf("decode: %q -> %q (should be: %q)", test[1], string(decoded), test[0]), "decode: %q -> %q (should be: %q)",
test[1],
string(decoded),
test[0],
) )
} }
} }
@@ -73,20 +50,37 @@ hex_decode :: proc(t: ^testing.T) {
@(test) @(test)
hex_decode_sequence :: proc(t: ^testing.T) { hex_decode_sequence :: proc(t: ^testing.T) {
b, ok := hex.decode_sequence("0x23") b, ok := hex.decode_sequence("0x23")
expect(t, ok, "decode_sequence: 0x23 not ok") testing.expect(t, ok, "decode_sequence: 0x23 not ok")
expect(t, b == '#', fmt.tprintf("decode_sequence: 0x23 -> %c (should be: %c)", b, '#')) testing.expectf(
t,
b == '#',
"decode_sequence: 0x23 -> %c (should be: %c)",
b,
'#',
)
b, ok = hex.decode_sequence("0X3F") b, ok = hex.decode_sequence("0X3F")
expect(t, ok, "decode_sequence: 0X3F not ok") testing.expect(t, ok, "decode_sequence: 0X3F not ok")
expect(t, b == '?', fmt.tprintf("decode_sequence: 0X3F -> %c (should be: %c)", b, '?')) testing.expectf(
t,
b == '?',
"decode_sequence: 0X3F -> %c (should be: %c)",
b,
'?',
)
b, ok = hex.decode_sequence("2a") b, ok = hex.decode_sequence("2a")
expect(t, ok, "decode_sequence: 2a not ok") testing.expect(t, ok, "decode_sequence: 2a not ok")
expect(t, b == '*', fmt.tprintf("decode_sequence: 2a -> %c (should be: %c)", b, '*')) testing.expectf(t,
b == '*',
"decode_sequence: 2a -> %c (should be: %c)",
b,
'*',
)
_, ok = hex.decode_sequence("1") _, ok = hex.decode_sequence("1")
expect(t, !ok, "decode_sequence: 1 should be too short") testing.expect(t, !ok, "decode_sequence: 1 should be too short")
_, ok = hex.decode_sequence("123") _, ok = hex.decode_sequence("123")
expect(t, !ok, "decode_sequence: 123 should be too long") testing.expect(t, !ok, "decode_sequence: 123 should be too long")
} }
+87 -115
View File
@@ -6,127 +6,99 @@ package test_core_hxa
import "core:encoding/hxa" import "core:encoding/hxa"
import "core:fmt" import "core:fmt"
import "core:testing" import "core:testing"
import tc "tests:common"
TEAPOT_PATH :: "core/assets/HXA/teapot.hxa" TEAPOT_PATH :: ODIN_ROOT + "tests/core/assets/HXA/teapot.hxa"
main :: proc() { import "core:os"
t := testing.T{}
test_read(&t)
test_write(&t)
tc.report(&t)
}
@test @test
test_read :: proc(t: ^testing.T) { test_read :: proc(t: ^testing.T) {
filename := tc.get_data_path(t, TEAPOT_PATH) data, _ := os.read_entire_file(TEAPOT_PATH)
defer delete(filename) // file, err := hxa.read_from_file(TEAPOT_PATH)
file, err := hxa.read(data)
file.backing = data
file.allocator = context.allocator
hxa.file_destroy(file)
// fmt.printfln("%#v", file)
file, err := hxa.read_from_file(filename)
e :: hxa.Read_Error.None e :: hxa.Read_Error.None
tc.expect(t, err == e, fmt.tprintf("%v: read_from_file(%v) -> %v != %v", #procedure, filename, err, e)) testing.expectf(t, err == e, "read_from_file(%v) -> %v != %v", TEAPOT_PATH, err, e)
defer hxa.file_destroy(file)
/* Header */ /* Header */
tc.expect(t, file.magic_number == 0x417848, fmt.tprintf("%v: file.magic_number %v != %v", testing.expectf(t, file.magic_number == 0x417848, "file.magic_number %v != %v", file.magic_number, 0x417848)
#procedure, file.magic_number, 0x417848)) testing.expectf(t, file.version == 1, "file.version %v != %v", file.version, 1)
tc.expect(t, file.version == 1, fmt.tprintf("%v: file.version %v != %v", testing.expectf(t, file.internal_node_count == 1, "file.internal_node_count %v != %v", file.internal_node_count, 1)
#procedure, file.version, 1))
tc.expect(t, file.internal_node_count == 1, fmt.tprintf("%v: file.internal_node_count %v != %v",
#procedure, file.internal_node_count, 1))
/* Nodes (only one) */ /* Nodes (only one) */
tc.expect(t, len(file.nodes) == 1, fmt.tprintf("%v: len(file.nodes) %v != %v", #procedure, len(file.nodes), 1)) testing.expectf(t, len(file.nodes) == 1, "len(file.nodes) %v != %v", len(file.nodes), 1)
m := &file.nodes[0].meta_data m := &file.nodes[0].meta_data
tc.expect(t, len(m^) == 38, fmt.tprintf("%v: len(m^) %v != %v", #procedure, len(m^), 38)) testing.expectf(t, len(m^) == 38, "len(m^) %v != %v", len(m^), 38)
{ {
e :: "Texture resolution" e :: "Texture resolution"
tc.expect(t, m[0].name == e, fmt.tprintf("%v: m[0].name %v != %v", #procedure, m[0].name, e)) testing.expectf(t, m[0].name == e, "m[0].name %v != %v", m[0].name, e)
m_v, m_v_ok := m[0].value.([]i64le) m_v, m_v_ok := m[0].value.([]i64le)
tc.expect(t, m_v_ok, fmt.tprintf("%v: m_v_ok %v != %v", #procedure, m_v_ok, true)) testing.expectf(t, m_v_ok, "m_v_ok %v != %v", m_v_ok, true)
tc.expect(t, len(m_v) == 1, fmt.tprintf("%v: len(m_v) %v != %v", #procedure, len(m_v), 1)) testing.expectf(t, len(m_v) == 1, "len(m_v) %v != %v", len(m_v), 1)
tc.expect(t, m_v[0] == 1024, fmt.tprintf("%v: m_v[0] %v != %v", #procedure, len(m_v), 1024)) testing.expectf(t, m_v[0] == 1024, "m_v[0] %v != %v", len(m_v), 1024)
} }
{ {
e :: "Validate" e :: "Validate"
tc.expect(t, m[37].name == e, fmt.tprintf("%v: m[37].name %v != %v", #procedure, m[37].name, e)) testing.expectf(t, m[37].name == e, "m[37].name %v != %v", m[37].name, e)
m_v, m_v_ok := m[37].value.([]i64le) m_v, m_v_ok := m[37].value.([]i64le)
tc.expect(t, m_v_ok, fmt.tprintf("%v: m_v_ok %v != %v", #procedure, m_v_ok, true)) testing.expectf(t, m_v_ok, "m_v_ok %v != %v", m_v_ok, true)
tc.expect(t, len(m_v) == 1, fmt.tprintf("%v: len(m_v) %v != %v", #procedure, len(m_v), 1)) testing.expectf(t, len(m_v) == 1, "len(m_v) %v != %v", len(m_v), 1)
tc.expect(t, m_v[0] == -2054847231, fmt.tprintf("%v: m_v[0] %v != %v", #procedure, len(m_v), -2054847231)) testing.expectf(t, m_v[0] == -2054847231, "m_v[0] %v != %v", len(m_v), -2054847231)
} }
/* Node content */ /* Node content */
v, v_ok := file.nodes[0].content.(hxa.Node_Geometry) v, v_ok := file.nodes[0].content.(hxa.Node_Geometry)
tc.expect(t, v_ok, fmt.tprintf("%v: v_ok %v != %v", #procedure, v_ok, true)) testing.expectf(t, v_ok, "v_ok %v != %v", v_ok, true)
tc.expect(t, v.vertex_count == 530, fmt.tprintf("%v: v.vertex_count %v != %v", #procedure, v.vertex_count, 530)) testing.expectf(t, v.vertex_count == 530, "v.vertex_count %v != %v", v.vertex_count, 530)
tc.expect(t, v.edge_corner_count == 2026, fmt.tprintf("%v: v.edge_corner_count %v != %v", testing.expectf(t, v.edge_corner_count == 2026, "v.edge_corner_count %v != %v", v.edge_corner_count, 2026)
#procedure, v.edge_corner_count, 2026)) testing.expectf(t, v.face_count == 517, "v.face_count %v != %v", v.face_count, 517)
tc.expect(t, v.face_count == 517, fmt.tprintf("%v: v.face_count %v != %v", #procedure, v.face_count, 517))
/* Vertex stack */ /* Vertex stack */
tc.expect(t, len(v.vertex_stack) == 1, fmt.tprintf("%v: len(v.vertex_stack) %v != %v", testing.expectf(t, len(v.vertex_stack) == 1, "len(v.vertex_stack) %v != %v", len(v.vertex_stack), 1)
#procedure, len(v.vertex_stack), 1))
{ {
e := "vertex" e := "vertex"
tc.expect(t, v.vertex_stack[0].name == e, fmt.tprintf("%v: v.vertex_stack[0].name %v != %v", testing.expectf(t, v.vertex_stack[0].name == e, "v.vertex_stack[0].name %v != %v", v.vertex_stack[0].name, e)
#procedure, v.vertex_stack[0].name, e))
} }
tc.expect(t, v.vertex_stack[0].components == 3, fmt.tprintf("%v: v.vertex_stack[0].components %v != %v", testing.expectf(t, v.vertex_stack[0].components == 3, "v.vertex_stack[0].components %v != %v", v.vertex_stack[0].components, 3)
#procedure, v.vertex_stack[0].components, 3))
/* Vertex stack data */ /* Vertex stack data */
vs_d, vs_d_ok := v.vertex_stack[0].data.([]f64le) vs_d, vs_d_ok := v.vertex_stack[0].data.([]f64le)
tc.expect(t, vs_d_ok, fmt.tprintf("%v: vs_d_ok %v != %v", #procedure, vs_d_ok, true)) testing.expectf(t, vs_d_ok, "vs_d_ok %v != %v", vs_d_ok, true)
tc.expect(t, len(vs_d) == 1590, fmt.tprintf("%v: len(vs_d) %v != %v", #procedure, len(vs_d), 1590)) testing.expectf(t, len(vs_d) == 1590, "len(vs_d) %v != %v", len(vs_d), 1590)
testing.expectf(t, vs_d[0] == 4.06266, "vs_d[0] %v (%h) != %v (%h)", vs_d[0], vs_d[0], 4.06266, 4.06266)
tc.expect(t, vs_d[0] == 4.06266, fmt.tprintf("%v: vs_d[0] %v (%h) != %v (%h)", testing.expectf(t, vs_d[1] == 2.83457, "vs_d[1] %v (%h) != %v (%h)", vs_d[1], vs_d[1], 2.83457, 2.83457)
#procedure, vs_d[0], vs_d[0], 4.06266, 4.06266)) testing.expectf(t, vs_d[2] == 0hbfbc5da6a4441787, "vs_d[2] %v (%h) != %v (%h)", vs_d[2], vs_d[2], 0hbfbc5da6a4441787, 0hbfbc5da6a4441787)
tc.expect(t, vs_d[1] == 2.83457, fmt.tprintf("%v: vs_d[1] %v (%h) != %v (%h)", testing.expectf(t, vs_d[3] == 0h4010074fb549f948, "vs_d[3] %v (%h) != %v (%h)", vs_d[3], vs_d[3], 0h4010074fb549f948, 0h4010074fb549f948)
#procedure, vs_d[1], vs_d[1], 2.83457, 2.83457)) testing.expectf(t, vs_d[1587] == 0h400befa82e87d2c7, "vs_d[1587] %v (%h) != %v (%h)", vs_d[1587], vs_d[1587], 0h400befa82e87d2c7, 0h400befa82e87d2c7)
tc.expect(t, vs_d[2] == 0hbfbc5da6a4441787, fmt.tprintf("%v: vs_d[2] %v (%h) != %v (%h)", testing.expectf(t, vs_d[1588] == 2.83457, "vs_d[1588] %v (%h) != %v (%h)", vs_d[1588], vs_d[1588], 2.83457, 2.83457)
#procedure, vs_d[2], vs_d[2], testing.expectf(t, vs_d[1589] == -1.56121, "vs_d[1589] %v (%h) != %v (%h)", vs_d[1589], vs_d[1589], -1.56121, -1.56121)
0hbfbc5da6a4441787, 0hbfbc5da6a4441787))
tc.expect(t, vs_d[3] == 0h4010074fb549f948, fmt.tprintf("%v: vs_d[3] %v (%h) != %v (%h)",
#procedure, vs_d[3], vs_d[3],
0h4010074fb549f948, 0h4010074fb549f948))
tc.expect(t, vs_d[1587] == 0h400befa82e87d2c7, fmt.tprintf("%v: vs_d[1587] %v (%h) != %v (%h)",
#procedure, vs_d[1587], vs_d[1587],
0h400befa82e87d2c7, 0h400befa82e87d2c7))
tc.expect(t, vs_d[1588] == 2.83457, fmt.tprintf("%v: vs_d[1588] %v (%h) != %v (%h)",
#procedure, vs_d[1588], vs_d[1588], 2.83457, 2.83457))
tc.expect(t, vs_d[1589] == -1.56121, fmt.tprintf("%v: vs_d[1589] %v (%h) != %v (%h)",
#procedure, vs_d[1589], vs_d[1589], -1.56121, -1.56121))
/* Corner stack */ /* Corner stack */
tc.expect(t, len(v.corner_stack) == 1, testing.expectf(t, len(v.corner_stack) == 1, "len(v.corner_stack) %v != %v", len(v.corner_stack), 1)
fmt.tprintf("%v: len(v.corner_stack) %v != %v", #procedure, len(v.corner_stack), 1))
{ {
e := "reference" e := "reference"
tc.expect(t, v.corner_stack[0].name == e, fmt.tprintf("%v: v.corner_stack[0].name %v != %v", testing.expectf(t, v.corner_stack[0].name == e, "v.corner_stack[0].name %v != %v", v.corner_stack[0].name, e)
#procedure, v.corner_stack[0].name, e))
} }
tc.expect(t, v.corner_stack[0].components == 1, fmt.tprintf("%v: v.corner_stack[0].components %v != %v", testing.expectf(t, v.corner_stack[0].components == 1, "v.corner_stack[0].components %v != %v", v.corner_stack[0].components, 1)
#procedure, v.corner_stack[0].components, 1))
/* Corner stack data */ /* Corner stack data */
cs_d, cs_d_ok := v.corner_stack[0].data.([]i32le) cs_d, cs_d_ok := v.corner_stack[0].data.([]i32le)
tc.expect(t, cs_d_ok, fmt.tprintf("%v: cs_d_ok %v != %v", #procedure, cs_d_ok, true)) testing.expectf(t, cs_d_ok, "cs_d_ok %v != %v", cs_d_ok, true)
tc.expect(t, len(cs_d) == 2026, fmt.tprintf("%v: len(cs_d) %v != %v", #procedure, len(cs_d), 2026)) testing.expectf(t, len(cs_d) == 2026, "len(cs_d) %v != %v", len(cs_d), 2026)
tc.expect(t, cs_d[0] == 6, fmt.tprintf("%v: cs_d[0] %v != %v", #procedure, cs_d[0], 6)) testing.expectf(t, cs_d[0] == 6, "cs_d[0] %v != %v", cs_d[0], 6)
tc.expect(t, cs_d[2025] == -32, fmt.tprintf("%v: cs_d[2025] %v != %v", #procedure, cs_d[2025], -32)) testing.expectf(t, cs_d[2025] == -32, "cs_d[2025] %v != %v", cs_d[2025], -32)
/* Edge and face stacks (empty) */ /* Edge and face stacks (empty) */
tc.expect(t, len(v.edge_stack) == 0, fmt.tprintf("%v: len(v.edge_stack) %v != %v", testing.expectf(t, len(v.edge_stack) == 0, "len(v.edge_stack) %v != %v", len(v.edge_stack), 0)
#procedure, len(v.edge_stack), 0)) testing.expectf(t, len(v.face_stack) == 0, "len(v.face_stack) %v != %v", len(v.face_stack), 0)
tc.expect(t, len(v.face_stack) == 0, fmt.tprintf("%v: len(v.face_stack) %v != %v",
#procedure, len(v.face_stack), 0))
} }
@test @test
@@ -154,72 +126,72 @@ test_write :: proc(t: ^testing.T) {
n, write_err := hxa.write(buf, w_file) n, write_err := hxa.write(buf, w_file)
write_e :: hxa.Write_Error.None write_e :: hxa.Write_Error.None
tc.expect(t, write_err == write_e, fmt.tprintf("%v: write_err %v != %v", #procedure, write_err, write_e)) testing.expectf(t, write_err == write_e, fmt.tprintf("write_err %v != %v", write_err, write_e))
tc.expect(t, n == required_size, fmt.tprintf("%v: n %v != %v", #procedure, n, required_size)) testing.expectf(t, n == required_size, fmt.tprintf("n %v != %v", n, required_size))
file, read_err := hxa.read(buf) file, read_err := hxa.read(buf)
read_e :: hxa.Read_Error.None read_e :: hxa.Read_Error.None
tc.expect(t, read_err == read_e, fmt.tprintf("%v: read_err %v != %v", #procedure, read_err, read_e)) testing.expectf(t, read_err == read_e, fmt.tprintf("read_err %v != %v", read_err, read_e))
defer hxa.file_destroy(file) defer hxa.file_destroy(file)
tc.expect(t, file.magic_number == 0x417848, fmt.tprintf("%v: file.magic_number %v != %v", testing.expectf(t, file.magic_number == 0x417848, fmt.tprintf("file.magic_number %v != %v",
#procedure, file.magic_number, 0x417848)) file.magic_number, 0x417848))
tc.expect(t, file.version == 3, fmt.tprintf("%v: file.version %v != %v", #procedure, file.version, 3)) testing.expectf(t, file.version == 3, fmt.tprintf("file.version %v != %v", file.version, 3))
tc.expect(t, file.internal_node_count == 1, fmt.tprintf("%v: file.internal_node_count %v != %v", testing.expectf(t, file.internal_node_count == 1, fmt.tprintf("file.internal_node_count %v != %v",
#procedure, file.internal_node_count, 1)) file.internal_node_count, 1))
tc.expect(t, len(file.nodes) == len(w_file.nodes), fmt.tprintf("%v: len(file.nodes) %v != %v", testing.expectf(t, len(file.nodes) == len(w_file.nodes), fmt.tprintf("len(file.nodes) %v != %v",
#procedure, len(file.nodes), len(w_file.nodes))) len(file.nodes), len(w_file.nodes)))
m := &file.nodes[0].meta_data m := &file.nodes[0].meta_data
w_m := &w_file.nodes[0].meta_data w_m := &w_file.nodes[0].meta_data
tc.expect(t, len(m^) == len(w_m^), fmt.tprintf("%v: len(m^) %v != %v", #procedure, len(m^), len(w_m^))) testing.expectf(t, len(m^) == len(w_m^), fmt.tprintf("len(m^) %v != %v", len(m^), len(w_m^)))
tc.expect(t, m[0].name == w_m[0].name, fmt.tprintf("%v: m[0].name %v != %v", #procedure, m[0].name, w_m[0].name)) testing.expectf(t, m[0].name == w_m[0].name, fmt.tprintf("m[0].name %v != %v", m[0].name, w_m[0].name))
m_v, m_v_ok := m[0].value.([]f64le) m_v, m_v_ok := m[0].value.([]f64le)
tc.expect(t, m_v_ok, fmt.tprintf("%v: m_v_ok %v != %v", #procedure, m_v_ok, true)) testing.expectf(t, m_v_ok, fmt.tprintf("m_v_ok %v != %v", m_v_ok, true))
tc.expect(t, len(m_v) == len(n1_m1_value), fmt.tprintf("%v: %v != len(m_v) %v", testing.expectf(t, len(m_v) == len(n1_m1_value), fmt.tprintf("%v != len(m_v) %v",
#procedure, len(m_v), len(n1_m1_value))) len(m_v), len(n1_m1_value)))
for i := 0; i < len(m_v); i += 1 { for i := 0; i < len(m_v); i += 1 {
tc.expect(t, m_v[i] == n1_m1_value[i], fmt.tprintf("%v: m_v[%d] %v != %v", testing.expectf(t, m_v[i] == n1_m1_value[i], fmt.tprintf("m_v[%d] %v != %v",
#procedure, i, m_v[i], n1_m1_value[i])) i, m_v[i], n1_m1_value[i]))
} }
v, v_ok := file.nodes[0].content.(hxa.Node_Image) v, v_ok := file.nodes[0].content.(hxa.Node_Image)
tc.expect(t, v_ok, fmt.tprintf("%v: v_ok %v != %v", #procedure, v_ok, true)) testing.expectf(t, v_ok, fmt.tprintf("v_ok %v != %v", v_ok, true))
tc.expect(t, v.type == n1_content.type, fmt.tprintf("%v: v.type %v != %v", #procedure, v.type, n1_content.type)) testing.expectf(t, v.type == n1_content.type, fmt.tprintf("v.type %v != %v", v.type, n1_content.type))
tc.expect(t, len(v.resolution) == 3, fmt.tprintf("%v: len(v.resolution) %v != %v", testing.expectf(t, len(v.resolution) == 3, fmt.tprintf("len(v.resolution) %v != %v",
#procedure, len(v.resolution), 3)) len(v.resolution), 3))
tc.expect(t, len(v.image_stack) == len(n1_content.image_stack), fmt.tprintf("%v: len(v.image_stack) %v != %v", testing.expectf(t, len(v.image_stack) == len(n1_content.image_stack), fmt.tprintf("len(v.image_stack) %v != %v",
#procedure, len(v.image_stack), len(n1_content.image_stack))) len(v.image_stack), len(n1_content.image_stack)))
for i := 0; i < len(v.image_stack); i += 1 { for i := 0; i < len(v.image_stack); i += 1 {
tc.expect(t, v.image_stack[i].name == n1_content.image_stack[i].name, testing.expectf(t, v.image_stack[i].name == n1_content.image_stack[i].name,
fmt.tprintf("%v: v.image_stack[%d].name %v != %v", fmt.tprintf("v.image_stack[%d].name %v != %v",
#procedure, i, v.image_stack[i].name, n1_content.image_stack[i].name)) i, v.image_stack[i].name, n1_content.image_stack[i].name))
tc.expect(t, v.image_stack[i].components == n1_content.image_stack[i].components, testing.expectf(t, v.image_stack[i].components == n1_content.image_stack[i].components,
fmt.tprintf("%v: v.image_stack[%d].components %v != %v", fmt.tprintf("v.image_stack[%d].components %v != %v",
#procedure, i, v.image_stack[i].components, n1_content.image_stack[i].components)) i, v.image_stack[i].components, n1_content.image_stack[i].components))
switch n1_t in n1_content.image_stack[i].data { switch n1_t in n1_content.image_stack[i].data {
case []u8: case []u8:
tc.expect(t, false, fmt.tprintf("%v: n1_content.image_stack[i].data []u8", #procedure)) testing.expectf(t, false, fmt.tprintf("n1_content.image_stack[i].data []u8", #procedure))
case []i32le: case []i32le:
tc.expect(t, false, fmt.tprintf("%v: n1_content.image_stack[i].data []i32le", #procedure)) testing.expectf(t, false, fmt.tprintf("n1_content.image_stack[i].data []i32le", #procedure))
case []f32le: case []f32le:
l, l_ok := v.image_stack[i].data.([]f32le) l, l_ok := v.image_stack[i].data.([]f32le)
tc.expect(t, l_ok, fmt.tprintf("%v: l_ok %v != %v", #procedure, l_ok, true)) testing.expectf(t, l_ok, fmt.tprintf("l_ok %v != %v", l_ok, true))
tc.expect(t, len(l) == len(n1_t), fmt.tprintf("%v: len(l) %v != %v", #procedure, len(l), len(n1_t))) testing.expectf(t, len(l) == len(n1_t), fmt.tprintf("len(l) %v != %v", len(l), len(n1_t)))
for j := 0; j < len(l); j += 1 { for j := 0; j < len(l); j += 1 {
tc.expect(t, l[j] == n1_t[j], fmt.tprintf("%v: l[%d] %v (%h) != %v (%h)", testing.expectf(t, l[j] == n1_t[j], fmt.tprintf("l[%d] %v (%h) != %v (%h)",
#procedure, j, l[j], l[j], n1_t[j], n1_t[j])) j, l[j], l[j], n1_t[j], n1_t[j]))
} }
case []f64le: case []f64le:
l, l_ok := v.image_stack[i].data.([]f64le) l, l_ok := v.image_stack[i].data.([]f64le)
tc.expect(t, l_ok, fmt.tprintf("%v: l_ok %v != %v", #procedure, l_ok, true)) testing.expectf(t, l_ok, fmt.tprintf("l_ok %v != %v", l_ok, true))
tc.expect(t, len(l) == len(n1_t), fmt.tprintf("%v: len(l) %v != %v", #procedure, len(l), len(n1_t))) testing.expectf(t, len(l) == len(n1_t), fmt.tprintf("len(l) %v != %v", len(l), len(n1_t)))
for j := 0; j < len(l); j += 1 { for j := 0; j < len(l); j += 1 {
tc.expect(t, l[j] == n1_t[j], fmt.tprintf("%v: l[%d] %v != %v", #procedure, j, l[j], n1_t[j])) testing.expectf(t, l[j] == n1_t[j], fmt.tprintf("l[%d] %v != %v", j, l[j], n1_t[j]))
} }
} }
} }
} }
+23 -65
View File
@@ -2,46 +2,8 @@ package test_core_json
import "core:encoding/json" import "core:encoding/json"
import "core:testing" import "core:testing"
import "core:fmt"
import "core:os"
import "core:mem/virtual" import "core:mem/virtual"
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
main :: proc() {
t := testing.T{}
parse_json(&t)
marshal_json(&t)
unmarshal_json(&t)
surrogate(&t)
utf8_string_of_multibyte_characters(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
@test @test
parse_json :: proc(t: ^testing.T) { parse_json :: proc(t: ^testing.T) {
@@ -72,10 +34,9 @@ parse_json :: proc(t: ^testing.T) {
} }
` `
_, err := json.parse(transmute([]u8)json_data) val, err := json.parse(transmute([]u8)json_data)
json.destroy_value(val)
msg := fmt.tprintf("Expected `json.parse` to return nil, got %v", err) testing.expectf(t, err == nil, "Expected `json.parse` to return nil, got %v", err)
expect(t, err == nil, msg)
} }
@test @test
@@ -83,7 +44,7 @@ out_of_memory_in_parse_json :: proc(t: ^testing.T) {
arena: virtual.Arena arena: virtual.Arena
arena_buffer: [256]byte arena_buffer: [256]byte
arena_init_error := virtual.arena_init_buffer(&arena, arena_buffer[:]) arena_init_error := virtual.arena_init_buffer(&arena, arena_buffer[:])
testing.expect(t, arena_init_error == nil, fmt.tprintf("Expected arena initialization to not return error, got: %v\n", arena_init_error)) testing.expectf(t, arena_init_error == nil, "Expected arena initialization to not return error, got: %v\n", arena_init_error)
context.allocator = virtual.arena_allocator(&arena) context.allocator = virtual.arena_allocator(&arena)
@@ -114,11 +75,11 @@ out_of_memory_in_parse_json :: proc(t: ^testing.T) {
} }
` `
_, err := json.parse(transmute([]u8)json_data) val, err := json.parse(transmute([]u8)json_data)
json.destroy_value(val)
expected_error := json.Error.Out_Of_Memory expected_error := json.Error.Out_Of_Memory
msg := fmt.tprintf("Expected `json.parse` to fail with %v, got %v", expected_error, err) testing.expectf(t, err == json.Error.Out_Of_Memory, "Expected `json.parse` to fail with %v, got %v", expected_error, err)
expect(t, err == json.Error.Out_Of_Memory, msg)
} }
@test @test
@@ -134,9 +95,9 @@ marshal_json :: proc(t: ^testing.T) {
b = 5, b = 5,
} }
_, err := json.marshal(my_struct) data, err := json.marshal(my_struct)
msg := fmt.tprintf("Expected `json.marshal` to return nil, got %v", err) defer delete(data)
expect(t, err == nil, msg) testing.expectf(t, err == nil, "Expected `json.marshal` to return nil, got %v", err)
} }
PRODUCTS := ` PRODUCTS := `
@@ -378,17 +339,12 @@ unmarshal_json :: proc(t: ^testing.T) {
err := json.unmarshal(transmute([]u8)PRODUCTS, &g, json.DEFAULT_SPECIFICATION) err := json.unmarshal(transmute([]u8)PRODUCTS, &g, json.DEFAULT_SPECIFICATION)
defer cleanup(g) defer cleanup(g)
msg := fmt.tprintf("Expected `json.unmarshal` to return nil, got %v", err) testing.expectf(t, err == nil, "Expected `json.unmarshal` to return nil, got %v", err)
expect(t, err == nil, msg) testing.expectf(t, len(g.products) == len(original_data.products), "Expected %v products to have been unmarshaled, got %v", len(original_data.products), len(g.products))
testing.expectf(t, original_data.cash == g.cash, "Expected cash to have been unmarshaled as %v, got %v", original_data.cash, g.cash)
msg = fmt.tprintf("Expected %v products to have been unmarshaled, got %v", len(original_data.products), len(g.products))
expect(t, len(g.products) == len(original_data.products), msg)
msg = fmt.tprintf("Expected cash to have been unmarshaled as %v, got %v", original_data.cash, g.cash)
expect(t, original_data.cash == g.cash, msg)
for p, i in g.products { for p, i in g.products {
expect(t, p == original_data.products[i], "Producted unmarshaled improperly") testing.expect(t, p == original_data.products[i], "Producted unmarshaled improperly")
} }
} }
@@ -397,17 +353,19 @@ surrogate :: proc(t: ^testing.T) {
input := `+ + * 😃 - /` input := `+ + * 😃 - /`
out, err := json.marshal(input) out, err := json.marshal(input)
expect(t, err == nil, fmt.tprintf("Expected `json.marshal(%q)` to return a nil error, got %v", input, err)) defer delete(out)
testing.expectf(t, err == nil, "Expected `json.marshal(%q)` to return a nil error, got %v", input, err)
back: string back: string
uerr := json.unmarshal(out, &back) uerr := json.unmarshal(out, &back)
expect(t, uerr == nil, fmt.tprintf("Expected `json.unmarshal(%q)` to return a nil error, got %v", string(out), uerr)) defer delete(back)
expect(t, back == input, fmt.tprintf("Expected `json.unmarshal(%q)` to return %q, got %v", string(out), input, uerr)) testing.expectf(t, uerr == nil, "Expected `json.unmarshal(%q)` to return a nil error, got %v", string(out), uerr)
testing.expectf(t, back == input, "Expected `json.unmarshal(%q)` to return %q, got %v", string(out), input, uerr)
} }
@test @test
utf8_string_of_multibyte_characters :: proc(t: ^testing.T) { utf8_string_of_multibyte_characters :: proc(t: ^testing.T) {
_, err := json.parse_string(`"🐛✅"`) val, err := json.parse_string(`"🐛✅"`)
msg := fmt.tprintf("Expected `json.parse` to return nil, got %v", err) defer json.destroy_value(val)
expect(t, err == nil, msg) testing.expectf(t, err == nil, "Expected `json.parse` to return nil, got %v", err)
} }
@@ -2,110 +2,74 @@ package test_core_varint
import "core:encoding/varint" import "core:encoding/varint"
import "core:testing" import "core:testing"
import "core:fmt"
import "core:os"
import "core:slice" import "core:slice"
import "core:math/rand" import "core:math/rand"
TEST_count := 0 NUM_RANDOM_TESTS_PER_BYTE_SIZE :: 10_000
TEST_fail := 0
RANDOM_TESTS :: 100
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
main :: proc() {
t := testing.T{}
test_leb128(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
@(test) @(test)
test_leb128 :: proc(t: ^testing.T) { test_uleb :: proc(t: ^testing.T) {
buf: [varint.LEB128_MAX_BYTES]u8 buf: [varint.LEB128_MAX_BYTES]u8
for vector in ULEB_Vectors { for vector in ULEB_Vectors {
val, size, err := varint.decode_uleb128(vector.encoded) val, size, err := varint.decode_uleb128(vector.encoded)
msg := fmt.tprintf("Expected %02x to decode to %v consuming %v bytes, got %v and %v", vector.encoded, vector.value, vector.size, val, size) testing.expectf(t, size == vector.size && val == vector.value, "Expected %02x to decode to %v consuming %v bytes, got %v and %v", vector.encoded, vector.value, vector.size, val, size)
expect(t, size == vector.size && val == vector.value, msg) testing.expectf(t, err == vector.error, "Expected decoder to return error %v, got %v for vector %v", vector.error, err, vector)
msg = fmt.tprintf("Expected decoder to return error %v, got %v for vector %v", vector.error, err, vector)
expect(t, err == vector.error, msg)
if err == .None { // Try to roundtrip if err == .None { // Try to roundtrip
size, err = varint.encode_uleb128(buf[:], vector.value) size, err = varint.encode_uleb128(buf[:], vector.value)
msg = fmt.tprintf("Expected %v to encode to %02x, got %02x", vector.value, vector.encoded, buf[:size]) testing.expectf(t, size == vector.size && slice.simple_equal(vector.encoded, buf[:size]), "Expected %v to encode to %02x, got %02x", vector.value, vector.encoded, buf[:size])
expect(t, size == vector.size && slice.simple_equal(vector.encoded, buf[:size]), msg)
} }
} }
}
@(test)
test_ileb :: proc(t: ^testing.T) {
buf: [varint.LEB128_MAX_BYTES]u8
for vector in ILEB_Vectors { for vector in ILEB_Vectors {
val, size, err := varint.decode_ileb128(vector.encoded) val, size, err := varint.decode_ileb128(vector.encoded)
msg := fmt.tprintf("Expected %02x to decode to %v consuming %v bytes, got %v and %v", vector.encoded, vector.value, vector.size, val, size) testing.expectf(t, size == vector.size && val == vector.value, "Expected %02x to decode to %v consuming %v bytes, got %v and %v", vector.encoded, vector.value, vector.size, val, size)
expect(t, size == vector.size && val == vector.value, msg) testing.expectf(t, err == vector.error, "Expected decoder to return error %v, got %v", vector.error, err)
msg = fmt.tprintf("Expected decoder to return error %v, got %v", vector.error, err)
expect(t, err == vector.error, msg)
if err == .None { // Try to roundtrip if err == .None { // Try to roundtrip
size, err = varint.encode_ileb128(buf[:], vector.value) size, err = varint.encode_ileb128(buf[:], vector.value)
msg = fmt.tprintf("Expected %v to encode to %02x, got %02x", vector.value, vector.encoded, buf[:size]) testing.expectf(t, size == vector.size && slice.simple_equal(vector.encoded, buf[:size]), "Expected %v to encode to %02x, got %02x", vector.value, vector.encoded, buf[:size])
expect(t, size == vector.size && slice.simple_equal(vector.encoded, buf[:size]), msg)
} }
} }
}
@(test)
test_random :: proc(t: ^testing.T) {
buf: [varint.LEB128_MAX_BYTES]u8
for num_bytes in 1..=uint(16) { for num_bytes in 1..=uint(16) {
for _ in 0..=RANDOM_TESTS { for _ in 0..=NUM_RANDOM_TESTS_PER_BYTE_SIZE {
unsigned, signed := get_random(num_bytes) unsigned, signed := get_random(num_bytes)
{ {
encode_size, encode_err := varint.encode_uleb128(buf[:], unsigned) encode_size, encode_err := varint.encode_uleb128(buf[:], unsigned)
msg := fmt.tprintf("%v failed to encode as an unsigned LEB128 value, got %v", unsigned, encode_err) testing.expectf(t, encode_err == .None, "%v failed to encode as an unsigned LEB128 value, got %v", unsigned, encode_err)
expect(t, encode_err == .None, msg)
decoded, decode_size, decode_err := varint.decode_uleb128(buf[:]) decoded, decode_size, decode_err := varint.decode_uleb128(buf[:])
msg = fmt.tprintf("Expected %02x to decode as %v, got %v", buf[:encode_size], unsigned, decoded) testing.expectf(t, decode_err == .None && decode_size == encode_size && decoded == unsigned, "Expected %02x to decode as %v, got %v", buf[:encode_size], unsigned, decoded)
expect(t, decode_err == .None && decode_size == encode_size && decoded == unsigned, msg)
} }
{ {
encode_size, encode_err := varint.encode_ileb128(buf[:], signed) encode_size, encode_err := varint.encode_ileb128(buf[:], signed)
msg := fmt.tprintf("%v failed to encode as a signed LEB128 value, got %v", signed, encode_err) testing.expectf(t, encode_err == .None, "%v failed to encode as a signed LEB128 value, got %v", signed, encode_err)
expect(t, encode_err == .None, msg)
decoded, decode_size, decode_err := varint.decode_ileb128(buf[:]) decoded, decode_size, decode_err := varint.decode_ileb128(buf[:])
msg = fmt.tprintf("Expected %02x to decode as %v, got %v, err: %v", buf[:encode_size], signed, decoded, decode_err) testing.expectf(t, decode_err == .None && decode_size == encode_size && decoded == signed, "Expected %02x to decode as %v, got %v, err: %v", buf[:encode_size], signed, decoded, decode_err)
expect(t, decode_err == .None && decode_size == encode_size && decoded == signed, msg)
} }
} }
} }
} }
@(private)
get_random :: proc(byte_count: uint) -> (u: u128, i: i128) { get_random :: proc(byte_count: uint) -> (u: u128, i: i128) {
assert(byte_count >= 0 && byte_count <= size_of(u128)) assert(byte_count >= 0 && byte_count <= size_of(u128))
+87 -139
View File
@@ -2,10 +2,10 @@ package test_core_xml
import "core:encoding/xml" import "core:encoding/xml"
import "core:testing" import "core:testing"
import "core:mem"
import "core:strings" import "core:strings"
import "core:io" import "core:io"
import "core:fmt" import "core:fmt"
import "core:log"
import "core:hash" import "core:hash"
Silent :: proc(pos: xml.Pos, format: string, args: ..any) {} Silent :: proc(pos: xml.Pos, format: string, args: ..any) {}
@@ -14,9 +14,6 @@ OPTIONS :: xml.Options{ flags = { .Ignore_Unsupported, .Intern_Comments, },
expected_doctype = "", expected_doctype = "",
} }
TEST_count := 0
TEST_fail := 0
TEST :: struct { TEST :: struct {
filename: string, filename: string,
options: xml.Options, options: xml.Options,
@@ -24,22 +21,14 @@ TEST :: struct {
crc32: u32, crc32: u32,
} }
/* TEST_SUITE_PATH :: ODIN_ROOT + "tests/core/assets/"
Relative to ODIN_ROOT
*/
TEST_FILE_PATH_PREFIX :: "tests/core/assets"
TESTS :: []TEST{ @(test)
/* xml_test_utf8_normal :: proc(t: ^testing.T) {
First we test that certain files parse without error. run_test(t, {
*/ // Tests UTF-8 idents and values.
// Test namespaced ident.
{ // Tests that nested partial CDATA start doesn't trip up parser.
/*
Tests UTF-8 idents and values.
Test namespaced ident.
Tests that nested partial CDATA start doesn't trip up parser.
*/
filename = "XML/utf8.xml", filename = "XML/utf8.xml",
options = { options = {
flags = { flags = {
@@ -48,13 +37,14 @@ TESTS :: []TEST{
expected_doctype = "恥ずべきフクロウ", expected_doctype = "恥ずべきフクロウ",
}, },
crc32 = 0xe9b62f03, crc32 = 0xe9b62f03,
}, })
}
{ @(test)
/* xml_test_utf8_unbox_cdata :: proc(t: ^testing.T) {
Same as above. run_test(t, {
Unbox CDATA in data tag. // Same as above.
*/ // Unbox CDATA in data tag.
filename = "XML/utf8.xml", filename = "XML/utf8.xml",
options = { options = {
flags = { flags = {
@@ -63,13 +53,14 @@ TESTS :: []TEST{
expected_doctype = "恥ずべきフクロウ", expected_doctype = "恥ずべきフクロウ",
}, },
crc32 = 0x9c2643ed, crc32 = 0x9c2643ed,
}, })
}
{ @(test)
/* xml_test_nl_qt_ts :: proc(t: ^testing.T) {
Simple Qt TS translation file. run_test(t, {
`core:i18n` requires it to be parsed properly. // Simple Qt TS translation file.
*/ // `core:i18n` requires it to be parsed properly.
filename = "I18N/nl_NL-qt-ts.ts", filename = "I18N/nl_NL-qt-ts.ts",
options = { options = {
flags = { flags = {
@@ -78,13 +69,14 @@ TESTS :: []TEST{
expected_doctype = "TS", expected_doctype = "TS",
}, },
crc32 = 0x859b7443, crc32 = 0x859b7443,
}, })
}
{ @(test)
/* xml_test_xliff_1_2 :: proc(t: ^testing.T) {
Simple XLiff 1.2 file. run_test(t, {
`core:i18n` requires it to be parsed properly. // Simple XLiff 1.2 file.
*/ // `core:i18n` requires it to be parsed properly.
filename = "I18N/nl_NL-xliff-1.2.xliff", filename = "I18N/nl_NL-xliff-1.2.xliff",
options = { options = {
flags = { flags = {
@@ -93,13 +85,14 @@ TESTS :: []TEST{
expected_doctype = "xliff", expected_doctype = "xliff",
}, },
crc32 = 0x3deaf329, crc32 = 0x3deaf329,
}, })
}
{ @(test)
/* xml_test_xliff_2_0 :: proc(t: ^testing.T) {
Simple XLiff 2.0 file. run_test(t, {
`core:i18n` requires it to be parsed properly. // Simple XLiff 2.0 file.
*/ // `core:i18n` requires it to be parsed properly.
filename = "I18N/nl_NL-xliff-2.0.xliff", filename = "I18N/nl_NL-xliff-2.0.xliff",
options = { options = {
flags = { flags = {
@@ -108,9 +101,12 @@ TESTS :: []TEST{
expected_doctype = "xliff", expected_doctype = "xliff",
}, },
crc32 = 0x0c55e287, crc32 = 0x0c55e287,
}, })
}
{ @(test)
xml_test_entities :: proc(t: ^testing.T) {
run_test(t, {
filename = "XML/entities.html", filename = "XML/entities.html",
options = { options = {
flags = { flags = {
@@ -119,9 +115,12 @@ TESTS :: []TEST{
expected_doctype = "html", expected_doctype = "html",
}, },
crc32 = 0x05373317, crc32 = 0x05373317,
}, })
}
{ @(test)
xml_test_entities_unbox :: proc(t: ^testing.T) {
run_test(t, {
filename = "XML/entities.html", filename = "XML/entities.html",
options = { options = {
flags = { flags = {
@@ -130,9 +129,12 @@ TESTS :: []TEST{
expected_doctype = "html", expected_doctype = "html",
}, },
crc32 = 0x3b6d4a90, crc32 = 0x3b6d4a90,
}, })
}
{ @(test)
xml_test_entities_unbox_decode :: proc(t: ^testing.T) {
run_test(t, {
filename = "XML/entities.html", filename = "XML/entities.html",
options = { options = {
flags = { flags = {
@@ -141,12 +143,12 @@ TESTS :: []TEST{
expected_doctype = "html", expected_doctype = "html",
}, },
crc32 = 0x5be2ffdc, crc32 = 0x5be2ffdc,
}, })
}
/* @(test)
Then we test that certain errors are returned as expected. xml_test_invalid_doctype :: proc(t: ^testing.T) {
*/ run_test(t, {
{
filename = "XML/utf8.xml", filename = "XML/utf8.xml",
options = { options = {
flags = { flags = {
@@ -156,12 +158,12 @@ TESTS :: []TEST{
}, },
err = .Invalid_DocType, err = .Invalid_DocType,
crc32 = 0x49b83d0a, crc32 = 0x49b83d0a,
}, })
}
/* @(test)
Parse the 9.08 MiB unicode.xml for good measure. xml_test_unicode :: proc(t: ^testing.T) {
*/ run_test(t, {
{
filename = "XML/unicode.xml", filename = "XML/unicode.xml",
options = { options = {
flags = { flags = {
@@ -171,39 +173,37 @@ TESTS :: []TEST{
}, },
err = .None, err = .None,
crc32 = 0x0b6100ab, crc32 = 0x0b6100ab,
}, })
} }
when ODIN_TEST { @(private)
expect :: testing.expect run_test :: proc(t: ^testing.T, test: TEST) {
log :: testing.log path := strings.concatenate({TEST_SUITE_PATH, test.filename})
} else { defer delete(path)
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1 doc, err := xml.load_from_file(path, test.options, Silent)
if !condition { defer xml.destroy(doc)
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message) tree_string := doc_to_string(doc)
return tree_bytes := transmute([]u8)tree_string
} defer delete(tree_bytes)
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) { crc32 := hash.crc32(tree_bytes)
fmt.printf("[%v] LOG:\n\t%v\n", loc, v)
failed := err != test.err
testing.expectf(t, err == test.err, "%v: Expected return value %v, got %v", test.filename, test.err, err)
failed |= crc32 != test.crc32
testing.expectf(t, crc32 == test.crc32, "%v: Expected CRC 0x%08x, got 0x%08x, with options %v", test.filename, test.crc32, crc32, test.options)
if failed {
// Don't fully print big trees.
tree_string = tree_string[:min(2_048, len(tree_string))]
log.error(tree_string)
} }
} }
test_file_path :: proc(filename: string) -> (path: string) { @(private)
path = fmt.tprintf("%v%v/%v", ODIN_ROOT, TEST_FILE_PATH_PREFIX, filename)
temp := transmute([]u8)path
for r, i in path {
if r == '\\' {
temp[i] = '/'
}
}
return path
}
doc_to_string :: proc(doc: ^xml.Document) -> (result: string) { doc_to_string :: proc(doc: ^xml.Document) -> (result: string) {
/* /*
Effectively a clone of the debug printer in the xml package. Effectively a clone of the debug printer in the xml package.
@@ -284,56 +284,4 @@ doc_to_string :: proc(doc: ^xml.Document) -> (result: string) {
print(strings.to_writer(&buf), doc) print(strings.to_writer(&buf), doc)
return strings.clone(strings.to_string(buf)) return strings.clone(strings.to_string(buf))
}
@test
run_tests :: proc(t: ^testing.T) {
for test in TESTS {
path := test_file_path(test.filename)
log(t, fmt.tprintf("Trying to parse %v", path))
doc, err := xml.load_from_file(path, test.options, Silent)
defer xml.destroy(doc)
tree_string := doc_to_string(doc)
tree_bytes := transmute([]u8)tree_string
defer delete(tree_bytes)
crc32 := hash.crc32(tree_bytes)
failed := err != test.err
err_msg := fmt.tprintf("Expected return value %v, got %v", test.err, err)
expect(t, err == test.err, err_msg)
failed |= crc32 != test.crc32
err_msg = fmt.tprintf("Expected CRC 0x%08x, got 0x%08x, with options %v", test.crc32, crc32, test.options)
expect(t, crc32 == test.crc32, err_msg)
if failed {
/*
Don't fully print big trees.
*/
tree_string = tree_string[:min(2_048, len(tree_string))]
fmt.println(tree_string)
}
}
}
main :: proc() {
t := testing.T{}
track: mem.Tracking_Allocator
mem.tracking_allocator_init(&track, context.allocator)
context.allocator = mem.tracking_allocator(&track)
run_tests(&t)
if len(track.allocation_map) > 0 {
for _, v in track.allocation_map {
err_msg := fmt.tprintf("%v Leaked %v bytes.", v.location, v.size)
expect(&t, false, err_msg)
}
}
fmt.printf("\n%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
} }
+20 -41
View File
@@ -2,48 +2,9 @@ package test_core_fmt
import "base:runtime" import "base:runtime"
import "core:fmt" import "core:fmt"
import "core:os"
import "core:testing"
import "core:math" import "core:math"
import "core:mem" import "core:mem"
import "core:testing"
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
main :: proc() {
t := testing.T{}
test_fmt_memory(&t)
test_fmt_doc_examples(&t)
test_fmt_options(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
check :: proc(t: ^testing.T, exp: string, format: string, args: ..any, loc := #caller_location) {
got := fmt.tprintf(format, ..args)
expect(t, got == exp, fmt.tprintf("(%q, %v): %q != %q", format, args, got, exp), loc)
}
@(test) @(test)
test_fmt_memory :: proc(t: ^testing.T) { test_fmt_memory :: proc(t: ^testing.T) {
@@ -141,7 +102,7 @@ test_fmt_doc_examples :: proc(t: ^testing.T) {
} }
@(test) @(test)
test_fmt_options :: proc(t: ^testing.T) { test_fmt_escaping_prefixes :: proc(t: ^testing.T) {
// Escaping // Escaping
check(t, "% { } 0 { } } {", "%% {{ }} {} {{ }} }} {{", 0 ) check(t, "% { } 0 { } } {", "%% {{ }} {} {{ }} }} {{", 0 )
@@ -152,7 +113,10 @@ test_fmt_options :: proc(t: ^testing.T) {
check(t, "+3", "%+i", 3 ) check(t, "+3", "%+i", 3 )
check(t, "0b11", "%#b", 3 ) check(t, "0b11", "%#b", 3 )
check(t, "0xA", "%#X", 10 ) check(t, "0xA", "%#X", 10 )
}
@(test)
test_fmt_indexing :: proc(t: ^testing.T) {
// Specific index formatting // Specific index formatting
check(t, "1 2 3", "%i %i %i", 1, 2, 3) check(t, "1 2 3", "%i %i %i", 1, 2, 3)
check(t, "1 2 3", "%[0]i %[1]i %[2]i", 1, 2, 3) check(t, "1 2 3", "%[0]i %[1]i %[2]i", 1, 2, 3)
@@ -161,7 +125,10 @@ test_fmt_options :: proc(t: ^testing.T) {
check(t, "1 2 3", "%i %[1]i %i", 1, 2, 3) check(t, "1 2 3", "%i %[1]i %i", 1, 2, 3)
check(t, "1 3 2", "%i %[2]i %i", 1, 2, 3) check(t, "1 3 2", "%i %[2]i %i", 1, 2, 3)
check(t, "1 1 1", "%[0]i %[0]i %[0]i", 1) check(t, "1 1 1", "%[0]i %[0]i %[0]i", 1)
}
@(test)
test_fmt_width_precision :: proc(t: ^testing.T) {
// Width // Width
check(t, "3.140", "%f", 3.14) check(t, "3.140", "%f", 3.14)
check(t, "3.140", "%4f", 3.14) check(t, "3.140", "%4f", 3.14)
@@ -199,7 +166,10 @@ test_fmt_options :: proc(t: ^testing.T) {
check(t, "3.140", "%*[1].*[2][0]f", 3.14, 5, 3) check(t, "3.140", "%*[1].*[2][0]f", 3.14, 5, 3)
check(t, "3.140", "%*[2].*[1]f", 3.14, 3, 5) check(t, "3.140", "%*[2].*[1]f", 3.14, 3, 5)
check(t, "3.140", "%5.*[1]f", 3.14, 3) check(t, "3.140", "%5.*[1]f", 3.14, 3)
}
@(test)
test_fmt_arg_errors :: proc(t: ^testing.T) {
// Error checking // Error checking
check(t, "%!(MISSING ARGUMENT)%!(NO VERB)", "%" ) check(t, "%!(MISSING ARGUMENT)%!(NO VERB)", "%" )
@@ -222,7 +192,10 @@ test_fmt_options :: proc(t: ^testing.T) {
check(t, "%!(BAD ARGUMENT NUMBER)%!(NO VERB)%!(EXTRA 0)", "%[1]", 0) check(t, "%!(BAD ARGUMENT NUMBER)%!(NO VERB)%!(EXTRA 0)", "%[1]", 0)
check(t, "3.1%!(EXTRA 3.14)", "%.1f", 3.14, 3.14) check(t, "3.1%!(EXTRA 3.14)", "%.1f", 3.14, 3.14)
}
@(test)
test_fmt_python_syntax :: proc(t: ^testing.T) {
// Python-like syntax // Python-like syntax
check(t, "1 2 3", "{} {} {}", 1, 2, 3) check(t, "1 2 3", "{} {} {}", 1, 2, 3)
check(t, "3 2 1", "{2} {1} {0}", 1, 2, 3) check(t, "3 2 1", "{2} {1} {0}", 1, 2, 3)
@@ -247,3 +220,9 @@ test_fmt_options :: proc(t: ^testing.T) {
check(t, "%!(MISSING CLOSE BRACE)%!(EXTRA 1)", "{", 1) check(t, "%!(MISSING CLOSE BRACE)%!(EXTRA 1)", "{", 1)
check(t, "%!(MISSING CLOSE BRACE)%!(EXTRA 1)", "{0", 1 ) check(t, "%!(MISSING CLOSE BRACE)%!(EXTRA 1)", "{0", 1 )
} }
@(private)
check :: proc(t: ^testing.T, exp: string, format: string, args: ..any) {
got := fmt.tprintf(format, ..args)
testing.expectf(t, got == exp, "(%q, %v): %q != %q", format, args, got, exp)
}
+38 -270
View File
@@ -2,201 +2,12 @@ package test_core_hash
import "core:hash/xxhash" import "core:hash/xxhash"
import "core:hash" import "core:hash"
import "core:time"
import "core:testing" import "core:testing"
import "core:fmt"
import "core:os"
import "core:math/rand" import "core:math/rand"
import "base:intrinsics" import "base:intrinsics"
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
main :: proc() {
t := testing.T{}
test_benchmark_runner(&t)
test_crc64_vectors(&t)
test_xxhash_vectors(&t)
test_xxhash_large(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
/*
Benchmarks
*/
setup_xxhash :: proc(options: ^time.Benchmark_Options, allocator := context.allocator) -> (err: time.Benchmark_Error) {
assert(options != nil)
options.input = make([]u8, options.bytes, allocator)
return nil if len(options.input) == options.bytes else .Allocation_Error
}
teardown_xxhash :: proc(options: ^time.Benchmark_Options, allocator := context.allocator) -> (err: time.Benchmark_Error) {
assert(options != nil)
delete(options.input)
return nil
}
benchmark_xxh32 :: proc(options: ^time.Benchmark_Options, allocator := context.allocator) -> (err: time.Benchmark_Error) {
buf := options.input
h: u32
for _ in 0..=options.rounds {
h = xxhash.XXH32(buf)
}
options.count = options.rounds
options.processed = options.rounds * options.bytes
options.hash = u128(h)
return nil
}
benchmark_xxh64 :: proc(options: ^time.Benchmark_Options, allocator := context.allocator) -> (err: time.Benchmark_Error) {
buf := options.input
h: u64
for _ in 0..=options.rounds {
h = xxhash.XXH64(buf)
}
options.count = options.rounds
options.processed = options.rounds * options.bytes
options.hash = u128(h)
return nil
}
benchmark_xxh3_64 :: proc(options: ^time.Benchmark_Options, allocator := context.allocator) -> (err: time.Benchmark_Error) {
buf := options.input
h: u64
for _ in 0..=options.rounds {
h = xxhash.XXH3_64(buf)
}
options.count = options.rounds
options.processed = options.rounds * options.bytes
options.hash = u128(h)
return nil
}
benchmark_xxh3_128 :: proc(options: ^time.Benchmark_Options, allocator := context.allocator) -> (err: time.Benchmark_Error) {
buf := options.input
h: u128
for _ in 0..=options.rounds {
h = xxhash.XXH3_128(buf)
}
options.count = options.rounds
options.processed = options.rounds * options.bytes
options.hash = h
return nil
}
benchmark_print :: proc(name: string, options: ^time.Benchmark_Options) {
fmt.printf("\t[%v] %v rounds, %v bytes processed in %v ns\n\t\t%5.3f rounds/s, %5.3f MiB/s\n",
name,
options.rounds,
options.processed,
time.duration_nanoseconds(options.duration),
options.rounds_per_second,
options.megabytes_per_second,
)
}
@test @test
test_benchmark_runner :: proc(t: ^testing.T) { test_xxhash_zero_fixed :: proc(t: ^testing.T) {
fmt.println("Starting benchmarks:")
name := "XXH32 100 zero bytes"
options := &time.Benchmark_Options{
rounds = 1_000,
bytes = 100,
setup = setup_xxhash,
bench = benchmark_xxh32,
teardown = teardown_xxhash,
}
err := time.benchmark(options, context.allocator)
expect(t, err == nil, name)
expect(t, options.hash == 0x85f6413c, name)
benchmark_print(name, options)
name = "XXH32 1 MiB zero bytes"
options.bytes = 1_048_576
err = time.benchmark(options, context.allocator)
expect(t, err == nil, name)
expect(t, options.hash == 0x9430f97f, name)
benchmark_print(name, options)
name = "XXH64 100 zero bytes"
options.bytes = 100
options.bench = benchmark_xxh64
err = time.benchmark(options, context.allocator)
expect(t, err == nil, name)
expect(t, options.hash == 0x17bb1103c92c502f, name)
benchmark_print(name, options)
name = "XXH64 1 MiB zero bytes"
options.bytes = 1_048_576
err = time.benchmark(options, context.allocator)
expect(t, err == nil, name)
expect(t, options.hash == 0x87d2a1b6e1163ef1, name)
benchmark_print(name, options)
name = "XXH3_64 100 zero bytes"
options.bytes = 100
options.bench = benchmark_xxh3_64
err = time.benchmark(options, context.allocator)
expect(t, err == nil, name)
expect(t, options.hash == 0x801fedc74ccd608c, name)
benchmark_print(name, options)
name = "XXH3_64 1 MiB zero bytes"
options.bytes = 1_048_576
err = time.benchmark(options, context.allocator)
expect(t, err == nil, name)
expect(t, options.hash == 0x918780b90550bf34, name)
benchmark_print(name, options)
name = "XXH3_128 100 zero bytes"
options.bytes = 100
options.bench = benchmark_xxh3_128
err = time.benchmark(options, context.allocator)
expect(t, err == nil, name)
expect(t, options.hash == 0x6ba30a4e9dffe1ff801fedc74ccd608c, name)
benchmark_print(name, options)
name = "XXH3_128 1 MiB zero bytes"
options.bytes = 1_048_576
err = time.benchmark(options, context.allocator)
expect(t, err == nil, name)
expect(t, options.hash == 0xb6ef17a3448492b6918780b90550bf34, name)
benchmark_print(name, options)
}
@test
test_xxhash_large :: proc(t: ^testing.T) {
many_zeroes := make([]u8, 16 * 1024 * 1024) many_zeroes := make([]u8, 16 * 1024 * 1024)
defer delete(many_zeroes) defer delete(many_zeroes)
@@ -204,62 +15,45 @@ test_xxhash_large :: proc(t: ^testing.T) {
for i, v in ZERO_VECTORS { for i, v in ZERO_VECTORS {
b := many_zeroes[:i] b := many_zeroes[:i]
fmt.printf("[test_xxhash_large] All at once. Size: %v\n", i)
xxh32 := xxhash.XXH32(b) xxh32 := xxhash.XXH32(b)
xxh64 := xxhash.XXH64(b) xxh64 := xxhash.XXH64(b)
xxh3_64 := xxhash.XXH3_64(b) xxh3_64 := xxhash.XXH3_64(b)
xxh3_128 := xxhash.XXH3_128(b) xxh3_128 := xxhash.XXH3_128(b)
xxh32_error := fmt.tprintf("[ XXH32(%03d) ] Expected: %08x. Got: %08x.", i, v.xxh_32, xxh32) testing.expectf(t, xxh32 == v.xxh_32, "[ XXH32(%03d) ] Expected: %08x, got: %08x", i, v.xxh_32, xxh32)
xxh64_error := fmt.tprintf("[ XXH64(%03d) ] Expected: %16x. Got: %16x.", i, v.xxh_64, xxh64) testing.expectf(t, xxh64 == v.xxh_64, "[ XXH64(%03d) ] Expected: %16x, got: %16x", i, v.xxh_64, xxh64)
xxh3_64_error := fmt.tprintf("[XXH3_64(%03d) ] Expected: %16x. Got: %16x.", i, v.xxh3_64, xxh3_64) testing.expectf(t, xxh3_64 == v.xxh3_64, "[XXH3_64(%03d) ] Expected: %16x, got: %16x", i, v.xxh3_64, xxh3_64)
xxh3_128_error := fmt.tprintf("[XXH3_128(%03d) ] Expected: %32x. Got: %32x.", i, v.xxh3_128, xxh3_128) testing.expectf(t, xxh3_128 == v.xxh3_128, "[XXH3_128(%03d) ] Expected: %32x, got: %32x", i, v.xxh3_128, xxh3_128)
expect(t, xxh32 == v.xxh_32, xxh32_error)
expect(t, xxh64 == v.xxh_64, xxh64_error)
expect(t, xxh3_64 == v.xxh3_64, xxh3_64_error)
expect(t, xxh3_128 == v.xxh3_128, xxh3_128_error)
} }
}
when #config(RAND_STATE, -1) >= 0 && #config(RAND_INC, -1) >= 0 { @(test)
random_seed := rand.Rand{ test_xxhash_zero_streamed_random_updates :: proc(t: ^testing.T) {
state = u64(#config(RAND_STATE, -1)), many_zeroes := make([]u8, 16 * 1024 * 1024)
inc = u64(#config(RAND_INC, -1)), defer delete(many_zeroes)
}
fmt.printf("Using user-selected seed {{%v,%v}} for update size randomness.\n", random_seed.state, random_seed.inc)
} else {
random_seed := rand.create(u64(intrinsics.read_cycle_counter()))
fmt.printf("Randonly selected seed {{%v,%v}} for update size randomness.\n", random_seed.state, random_seed.inc)
}
// Streamed // Streamed
for i, v in ZERO_VECTORS { for i, v in ZERO_VECTORS {
b := many_zeroes[:i] b := many_zeroes[:i]
fmt.printf("[test_xxhash_large] Streamed. Size: %v\n", i)
// bytes_per_update := []int{1, 42, 13, 7, 16, 5, 23, 74, 1024, 511, 1023, 47}
// update_size_idx: int
xxh_32_state, xxh_32_err := xxhash.XXH32_create_state() xxh_32_state, xxh_32_err := xxhash.XXH32_create_state()
defer xxhash.XXH32_destroy_state(xxh_32_state) defer xxhash.XXH32_destroy_state(xxh_32_state)
expect(t, xxh_32_err == nil, "Problem initializing XXH_32 state.") testing.expect(t, xxh_32_err == nil, "Problem initializing XXH_32 state")
xxh_64_state, xxh_64_err := xxhash.XXH64_create_state() xxh_64_state, xxh_64_err := xxhash.XXH64_create_state()
defer xxhash.XXH64_destroy_state(xxh_64_state) defer xxhash.XXH64_destroy_state(xxh_64_state)
expect(t, xxh_64_err == nil, "Problem initializing XXH_64 state.") testing.expect(t, xxh_64_err == nil, "Problem initializing XXH_64 state")
xxh3_64_state, xxh3_64_err := xxhash.XXH3_create_state() xxh3_64_state, xxh3_64_err := xxhash.XXH3_create_state()
defer xxhash.XXH3_destroy_state(xxh3_64_state) defer xxhash.XXH3_destroy_state(xxh3_64_state)
expect(t, xxh3_64_err == nil, "Problem initializing XXH3_64 state.") testing.expect(t, xxh3_64_err == nil, "Problem initializing XXH3_64 state")
xxh3_128_state, xxh3_128_err := xxhash.XXH3_create_state() xxh3_128_state, xxh3_128_err := xxhash.XXH3_create_state()
defer xxhash.XXH3_destroy_state(xxh3_128_state) defer xxhash.XXH3_destroy_state(xxh3_128_state)
expect(t, xxh3_128_err == nil, "Problem initializing XXH3_128 state.") testing.expect(t, xxh3_128_err == nil, "Problem initializing XXH3_128 state")
// XXH3_128_update // XXH3_128_update
random_seed := rand.create(t.seed)
for len(b) > 0 { for len(b) > 0 {
update_size := min(len(b), rand.int_max(8192, &random_seed)) update_size := min(len(b), rand.int_max(8192, &random_seed))
if update_size > 4096 { if update_size > 4096 {
@@ -281,28 +75,19 @@ test_xxhash_large :: proc(t: ^testing.T) {
xxh3_64 := xxhash.XXH3_64_digest(xxh3_64_state) xxh3_64 := xxhash.XXH3_64_digest(xxh3_64_state)
xxh3_128 := xxhash.XXH3_128_digest(xxh3_128_state) xxh3_128 := xxhash.XXH3_128_digest(xxh3_128_state)
xxh32_error := fmt.tprintf("[ XXH32(%03d) ] Expected: %08x. Got: %08x.", i, v.xxh_32, xxh32) testing.expectf(t, xxh32 == v.xxh_32, "[ XXH32(%03d) ] Expected: %08x, got: %08x", i, v.xxh_32, xxh32)
xxh64_error := fmt.tprintf("[ XXH64(%03d) ] Expected: %16x. Got: %16x.", i, v.xxh_64, xxh64) testing.expectf(t, xxh64 == v.xxh_64, "[ XXH64(%03d) ] Expected: %16x, got: %16x", i, v.xxh_64, xxh64)
xxh3_64_error := fmt.tprintf("[XXH3_64(%03d) ] Expected: %16x. Got: %16x.", i, v.xxh3_64, xxh3_64) testing.expectf(t, xxh3_64 == v.xxh3_64, "[XXH3_64(%03d) ] Expected: %16x, got: %16x", i, v.xxh3_64, xxh3_64)
xxh3_128_error := fmt.tprintf("[XXH3_128(%03d) ] Expected: %32x. Got: %32x.", i, v.xxh3_128, xxh3_128) testing.expectf(t, xxh3_128 == v.xxh3_128, "[XXH3_128(%03d) ] Expected: %32x, got: %32x", i, v.xxh3_128, xxh3_128)
expect(t, xxh32 == v.xxh_32, xxh32_error)
expect(t, xxh64 == v.xxh_64, xxh64_error)
expect(t, xxh3_64 == v.xxh3_64, xxh3_64_error)
expect(t, xxh3_128 == v.xxh3_128, xxh3_128_error)
} }
} }
@test @test
test_xxhash_vectors :: proc(t: ^testing.T) { test_xxhash_seeded :: proc(t: ^testing.T) {
fmt.println("Verifying against XXHASH_TEST_VECTOR_SEEDED:")
buf := make([]u8, 256) buf := make([]u8, 256)
defer delete(buf) defer delete(buf)
for seed, table in XXHASH_TEST_VECTOR_SEEDED { for seed, table in XXHASH_TEST_VECTOR_SEEDED {
fmt.printf("\tSeed: %v\n", seed)
for v, i in table { for v, i in table {
b := buf[:i] b := buf[:i]
@@ -311,60 +96,48 @@ test_xxhash_vectors :: proc(t: ^testing.T) {
xxh3_64 := xxhash.XXH3_64(b, seed) xxh3_64 := xxhash.XXH3_64(b, seed)
xxh3_128 := xxhash.XXH3_128(b, seed) xxh3_128 := xxhash.XXH3_128(b, seed)
xxh32_error := fmt.tprintf("[ XXH32(%03d) ] Expected: %08x. Got: %08x.", i, v.xxh_32, xxh32) testing.expectf(t, xxh32 == v.xxh_32, "[ XXH32(%03d) ] Expected: %08x, got: %08x", i, v.xxh_32, xxh32)
xxh64_error := fmt.tprintf("[ XXH64(%03d) ] Expected: %16x. Got: %16x.", i, v.xxh_64, xxh64) testing.expectf(t, xxh64 == v.xxh_64, "[ XXH64(%03d) ] Expected: %16x, got: %16x", i, v.xxh_64, xxh64)
testing.expectf(t, xxh3_64 == v.xxh3_64, "[XXH3_64(%03d) ] Expected: %16x, got: %16x", i, v.xxh3_64, xxh3_64)
xxh3_64_error := fmt.tprintf("[XXH3_64(%03d) ] Expected: %16x. Got: %16x.", i, v.xxh3_64, xxh3_64) testing.expectf(t, xxh3_128 == v.xxh3_128, "[XXH3_128(%03d) ] Expected: %32x, got: %32x", i, v.xxh3_128, xxh3_128)
xxh3_128_error := fmt.tprintf("[XXH3_128(%03d) ] Expected: %32x. Got: %32x.", i, v.xxh3_128, xxh3_128)
expect(t, xxh32 == v.xxh_32, xxh32_error)
expect(t, xxh64 == v.xxh_64, xxh64_error)
expect(t, xxh3_64 == v.xxh3_64, xxh3_64_error)
expect(t, xxh3_128 == v.xxh3_128, xxh3_128_error)
if len(b) > xxhash.XXH3_MIDSIZE_MAX { if len(b) > xxhash.XXH3_MIDSIZE_MAX {
fmt.printf("XXH3 - size: %v\n", len(b))
xxh3_state, _ := xxhash.XXH3_create_state() xxh3_state, _ := xxhash.XXH3_create_state()
xxhash.XXH3_64_reset_with_seed(xxh3_state, seed) xxhash.XXH3_64_reset_with_seed(xxh3_state, seed)
xxhash.XXH3_64_update(xxh3_state, b) xxhash.XXH3_64_update(xxh3_state, b)
xxh3_64_streamed := xxhash.XXH3_64_digest(xxh3_state) xxh3_64_streamed := xxhash.XXH3_64_digest(xxh3_state)
xxhash.XXH3_destroy_state(xxh3_state) xxhash.XXH3_destroy_state(xxh3_state)
xxh3_64s_error := fmt.tprintf("[XXH3_64s(%03d) ] Expected: %16x. Got: %16x.", i, v.xxh3_64, xxh3_64_streamed) testing.expectf(t, xxh3_64_streamed == v.xxh3_64, "[XXH3_64s(%03d) ] Expected: %16x, got: %16x", i, v.xxh3_64, xxh3_64_streamed)
expect(t, xxh3_64_streamed == v.xxh3_64, xxh3_64s_error)
xxh3_state2, _ := xxhash.XXH3_create_state() xxh3_state2, _ := xxhash.XXH3_create_state()
xxhash.XXH3_128_reset_with_seed(xxh3_state2, seed) xxhash.XXH3_128_reset_with_seed(xxh3_state2, seed)
xxhash.XXH3_128_update(xxh3_state2, b) xxhash.XXH3_128_update(xxh3_state2, b)
xxh3_128_streamed := xxhash.XXH3_128_digest(xxh3_state2) xxh3_128_streamed := xxhash.XXH3_128_digest(xxh3_state2)
xxhash.XXH3_destroy_state(xxh3_state2) xxhash.XXH3_destroy_state(xxh3_state2)
xxh3_128s_error := fmt.tprintf("[XXH3_128s(%03d) ] Expected: %32x. Got: %32x.", i, v.xxh3_128, xxh3_128_streamed) testing.expectf(t, xxh3_128_streamed == v.xxh3_128, "[XXH3_128s(%03d) ] Expected: %32x, got: %32x", i, v.xxh3_128, xxh3_128_streamed)
expect(t, xxh3_128_streamed == v.xxh3_128, xxh3_128s_error)
} }
} }
} }
}
@test
test_xxhash_secret :: proc(t: ^testing.T) {
buf := make([]u8, 256)
defer delete(buf)
fmt.println("Verifying against XXHASH_TEST_VECTOR_SECRET:")
for secret, table in XXHASH_TEST_VECTOR_SECRET { for secret, table in XXHASH_TEST_VECTOR_SECRET {
fmt.printf("\tSecret:\n\t\t\"%v\"\n", secret)
secret_bytes := transmute([]u8)secret secret_bytes := transmute([]u8)secret
for v, i in table { for v, i in table {
b := buf[:i] b := buf[:i]
xxh3_128 := xxhash.XXH3_128(b, secret_bytes) xxh3_128 := xxhash.XXH3_128(b, secret_bytes)
xxh3_128_error := fmt.tprintf("[XXH3_128(%03d)] Expected: %32x. Got: %32x.", i, v.xxh3_128_secret, xxh3_128) testing.expectf(t, xxh3_128 == v.xxh3_128_secret, "[XXH3_128(%03d)] Expected: %32x, got: %32x", i, v.xxh3_128_secret, xxh3_128)
expect(t, xxh3_128 == v.xxh3_128_secret, xxh3_128_error)
} }
} }
} }
@test @test
test_crc64_vectors :: proc(t: ^testing.T) { test_crc64_vectors :: proc(t: ^testing.T) {
fmt.println("Verifying CRC-64:")
vectors := map[string][4]u64 { vectors := map[string][4]u64 {
"123456789" = { "123456789" = {
0x6c40df5f0b497347, // ECMA-182, 0x6c40df5f0b497347, // ECMA-182,
@@ -379,23 +152,18 @@ test_crc64_vectors :: proc(t: ^testing.T) {
0xe7fcf1006b503b61, // ISO 3306, input and output inverted 0xe7fcf1006b503b61, // ISO 3306, input and output inverted
}, },
} }
defer delete(vectors)
for vector, expected in vectors { for vector, expected in vectors {
fmt.println("\tVector:", vector)
b := transmute([]u8)vector b := transmute([]u8)vector
ecma := hash.crc64_ecma_182(b) ecma := hash.crc64_ecma_182(b)
xz := hash.crc64_xz(b) xz := hash.crc64_xz(b)
iso := hash.crc64_iso_3306(b) iso := hash.crc64_iso_3306(b)
iso2 := hash.crc64_iso_3306_inverse(b) iso2 := hash.crc64_iso_3306_inverse(b)
ecma_error := fmt.tprintf("[ CRC-64 ECMA ] Expected: %016x. Got: %016x.", expected[0], ecma) testing.expectf(t, ecma == expected[0], "[ CRC-64 ECMA ] Expected: %016x, got: %016x", expected[0], ecma)
xz_error := fmt.tprintf("[ CRC-64 XZ ] Expected: %016x. Got: %016x.", expected[1], xz) testing.expectf(t, xz == expected[1], "[ CRC-64 XZ ] Expected: %016x, got: %016x", expected[1], xz)
iso_error := fmt.tprintf("[ CRC-64 ISO 3306] Expected: %016x. Got: %016x.", expected[2], iso) testing.expectf(t, iso == expected[2], "[ CRC-64 ISO 3306] Expected: %016x, got: %016x", expected[2], iso)
iso2_error := fmt.tprintf("[~CRC-64 ISO 3306] Expected: %016x. Got: %016x.", expected[3], iso2) testing.expectf(t, iso2 == expected[3], "[~CRC-64 ISO 3306] Expected: %016x, got: %016x", expected[3], iso2)
expect(t, ecma == expected[0], ecma_error)
expect(t, xz == expected[1], xz_error)
expect(t, iso == expected[2], iso_error)
expect(t, iso2 == expected[3], iso2_error)
} }
} }
+2 -4
View File
@@ -1,6 +1,4 @@
/* // Hash Test Vectors
Hash Test Vectors
*/
package test_core_hash package test_core_hash
XXHASH_Test_Vectors :: struct #packed { XXHASH_Test_Vectors :: struct #packed {
@@ -6789,4 +6787,4 @@ XXHASH_TEST_VECTOR_SECRET := map[string][257]XXHASH_Test_Vectors_With_Secret{
/* XXH3_128_with_secret */ 0x0f9b41191242ade48bbde48dff0d38ec, /* XXH3_128_with_secret */ 0x0f9b41191242ade48bbde48dff0d38ec,
}, },
}, },
} }
+2 -4
View File
@@ -1,4 +1,2 @@
@echo off @echo off
pushd .. odin test . -define:ODIN_TEST_TRACK_MEMORY=true -define:ODIN_TEST_PROGRESS_WIDTH=12 -vet -strict-style
odin run image
popd
+115 -194
View File
@@ -20,49 +20,14 @@ import "core:image/tga"
import "core:bytes" import "core:bytes"
import "core:hash" import "core:hash"
import "core:fmt"
import "core:strings" import "core:strings"
import "core:mem" import "core:mem"
import "core:os"
import "core:time" import "core:time"
import "base:runtime" TEST_SUITE_PATH :: ODIN_ROOT + "tests/core/assets/PNG/"
TEST_SUITE_PATH :: "assets/PNG"
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
I_Error :: image.Error I_Error :: image.Error
main :: proc() {
t := testing.T{}
png_test(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
PNG_Test :: struct { PNG_Test :: struct {
file: string, file: string,
tests: []struct { tests: []struct {
@@ -72,7 +37,6 @@ PNG_Test :: struct {
hash: u32, hash: u32,
}, },
} }
Default :: image.Options{} Default :: image.Options{}
Alpha_Add :: image.Options{.alpha_add_if_missing} Alpha_Add :: image.Options{.alpha_add_if_missing}
Premul_Drop :: image.Options{.alpha_premultiply, .alpha_drop_if_present} Premul_Drop :: image.Options{.alpha_premultiply, .alpha_drop_if_present}
@@ -82,7 +46,7 @@ Blend_BG_Keep :: image.Options{.blend_background, .alpha_add_if_missing}
Return_Metadata :: image.Options{.return_metadata} Return_Metadata :: image.Options{.return_metadata}
No_Channel_Expansion :: image.Options{.do_not_expand_channels, .return_metadata} No_Channel_Expansion :: image.Options{.do_not_expand_channels, .return_metadata}
PNG_Dims :: struct { PNG_Dims :: struct {
width: int, width: int,
height: int, height: int,
channels: int, channels: int,
@@ -1430,81 +1394,94 @@ Expected_Text := map[string]map[string]png.Text {
} }
@test @test
png_test :: proc(t: ^testing.T) { png_test_basic :: proc(t: ^testing.T) {
run_png_suite(t, Basic_PNG_Tests)
total_tests := 0
total_expected := 235
PNG_Suites := [][]PNG_Test{
Basic_PNG_Tests,
Interlaced_PNG_Tests,
Odd_Sized_PNG_Tests,
PNG_bKGD_Tests,
PNG_tRNS_Tests,
PNG_Filter_Tests,
PNG_Varied_IDAT_Tests,
PNG_ZLIB_Levels_Tests,
PNG_sPAL_Tests,
PNG_Ancillary_Tests,
Corrupt_PNG_Tests,
No_Postprocesing_Tests,
}
for suite in PNG_Suites {
total_tests += run_png_suite(t, suite)
}
error := fmt.tprintf("Expected %v PNG tests, %v ran.", total_expected, total_tests)
expect(t, total_tests == total_expected, error)
} }
run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) { @test
png_test_interlaced :: proc(t: ^testing.T) {
run_png_suite(t, Interlaced_PNG_Tests)
}
context = runtime.default_context() @test
png_test_odd_sized :: proc(t: ^testing.T) {
run_png_suite(t, Odd_Sized_PNG_Tests)
}
@test
png_test_bKGD :: proc(t: ^testing.T) {
run_png_suite(t, PNG_bKGD_Tests)
}
@test
png_test_tRNS :: proc(t: ^testing.T) {
run_png_suite(t, PNG_tRNS_Tests)
}
@test
png_test_sPAL :: proc(t: ^testing.T) {
run_png_suite(t, PNG_sPAL_Tests)
}
@test
png_test_filter :: proc(t: ^testing.T) {
run_png_suite(t, PNG_Filter_Tests)
}
@test
png_test_varied_idat :: proc(t: ^testing.T) {
run_png_suite(t, PNG_Varied_IDAT_Tests)
}
@test
png_test_zlib_levels :: proc(t: ^testing.T) {
run_png_suite(t, PNG_ZLIB_Levels_Tests)
}
@test
png_test_ancillary :: proc(t: ^testing.T) {
run_png_suite(t, PNG_Ancillary_Tests)
}
@test
png_test_corrupt :: proc(t: ^testing.T) {
run_png_suite(t, Corrupt_PNG_Tests)
}
@test
png_test_no_postproc :: proc(t: ^testing.T) {
run_png_suite(t, No_Postprocesing_Tests)
}
run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
for file in suite { for file in suite {
test_file := strings.concatenate({TEST_SUITE_PATH, "/", file.file, ".png"}, context.temp_allocator) test_file := strings.concatenate({TEST_SUITE_PATH, file.file, ".png"})
defer delete(test_file)
img: ^png.Image img: ^png.Image
err: png.Error err: png.Error
count := 0 count := 0
for test in file.tests { for test in file.tests {
count += 1 count += 1
subtotal += 1
passed := false
track: mem.Tracking_Allocator
mem.tracking_allocator_init(&track, context.allocator)
context.allocator = mem.tracking_allocator(&track)
img, err = png.load(test_file, test.options) img, err = png.load(test_file, test.options)
error := fmt.tprintf("%v failed with %v.", test_file, err) passed := (test.expected_error == nil && err == nil) || (test.expected_error == err)
testing.expectf(t, passed, "%v failed with %v.", test_file, err)
passed = (test.expected_error == nil && err == nil) || (test.expected_error == err)
expect(t, passed, error)
if err == nil { // No point in running the other tests if it didn't load. if err == nil { // No point in running the other tests if it didn't load.
pixels := bytes.buffer_to_bytes(&img.pixels) pixels := bytes.buffer_to_bytes(&img.pixels)
// This struct compare fails at -opt:2 if PNG_Dims is not #packed. // This struct compare fails at -opt:2 if PNG_Dims is not #packed.
dims := PNG_Dims{img.width, img.height, img.channels, img.depth}
dims := PNG_Dims{img.width, img.height, img.channels, img.depth}
error = fmt.tprintf("%v has %v, expected: %v.", file.file, dims, test.dims)
dims_pass := test.dims == dims dims_pass := test.dims == dims
expect(t, dims_pass, error) testing.expectf(t, dims_pass, "%v has %v, expected: %v.", file.file, dims, test.dims)
passed &= dims_pass passed &= dims_pass
png_hash := hash.crc32(pixels) png_hash := hash.crc32(pixels)
error = fmt.tprintf("%v test %v hash is %08x, expected %08x with %v.", file.file, count, png_hash, test.hash, test.options) testing.expectf(t, test.hash == png_hash, "%v test %v hash is %08x, expected %08x with %v.", file.file, count, png_hash, test.hash, test.options)
expect(t, test.hash == png_hash, error)
passed &= test.hash == png_hash passed &= test.hash == png_hash
@@ -1515,19 +1492,16 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
defer bytes.buffer_destroy(&qoi_buffer) defer bytes.buffer_destroy(&qoi_buffer)
qoi_save_err := qoi.save(&qoi_buffer, img) qoi_save_err := qoi.save(&qoi_buffer, img)
error = fmt.tprintf("%v test %v QOI save failed with %v.", file.file, count, qoi_save_err) testing.expectf(t, qoi_save_err == nil, "%v test %v QOI save failed with %v.", file.file, count, qoi_save_err)
expect(t, qoi_save_err == nil, error)
if qoi_save_err == nil { if qoi_save_err == nil {
qoi_img, qoi_load_err := qoi.load(qoi_buffer.buf[:]) qoi_img, qoi_load_err := qoi.load(qoi_buffer.buf[:])
defer qoi.destroy(qoi_img) defer qoi.destroy(qoi_img)
error = fmt.tprintf("%v test %v QOI load failed with %v.", file.file, count, qoi_load_err) testing.expectf(t, qoi_load_err == nil, "%v test %v QOI load failed with %v.", file.file, count, qoi_load_err)
expect(t, qoi_load_err == nil, error)
qoi_hash := hash.crc32(qoi_img.pixels.buf[:]) qoi_hash := hash.crc32(qoi_img.pixels.buf[:])
error = fmt.tprintf("%v test %v QOI load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, qoi_hash, png_hash, test.options) testing.expectf(t, qoi_hash == png_hash, "%v test %v QOI load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, qoi_hash, png_hash, test.options)
expect(t, qoi_hash == png_hash, error)
} }
} }
@@ -1537,19 +1511,15 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
defer bytes.buffer_destroy(&tga_buffer) defer bytes.buffer_destroy(&tga_buffer)
tga_save_err := tga.save(&tga_buffer, img) tga_save_err := tga.save(&tga_buffer, img)
error = fmt.tprintf("%v test %v TGA save failed with %v.", file.file, count, tga_save_err) testing.expectf(t, tga_save_err == nil, "%v test %v TGA save failed with %v.", file.file, count, tga_save_err)
expect(t, tga_save_err == nil, error)
if tga_save_err == nil { if tga_save_err == nil {
tga_img, tga_load_err := tga.load(tga_buffer.buf[:]) tga_img, tga_load_err := tga.load(tga_buffer.buf[:])
defer tga.destroy(tga_img) defer tga.destroy(tga_img)
error = fmt.tprintf("%v test %v TGA load failed with %v.", file.file, count, tga_load_err) testing.expectf(t, tga_load_err == nil, "%v test %v TGA load failed with %v.", file.file, count, tga_load_err)
expect(t, tga_load_err == nil, error)
tga_hash := hash.crc32(tga_img.pixels.buf[:]) tga_hash := hash.crc32(tga_img.pixels.buf[:])
error = fmt.tprintf("%v test %v TGA load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, tga_hash, png_hash, test.options) testing.expectf(t, tga_hash == png_hash, "%v test %v TGA load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, tga_hash, png_hash, test.options)
expect(t, tga_hash == png_hash, error)
} }
} }
@@ -1558,22 +1528,18 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
pbm_buf, pbm_save_err := pbm.save_to_buffer(img) pbm_buf, pbm_save_err := pbm.save_to_buffer(img)
defer delete(pbm_buf) defer delete(pbm_buf)
error = fmt.tprintf("%v test %v PBM save failed with %v.", file.file, count, pbm_save_err) testing.expectf(t, pbm_save_err == nil, "%v test %v PBM save failed with %v.", file.file, count, pbm_save_err)
expect(t, pbm_save_err == nil, error)
if pbm_save_err == nil { if pbm_save_err == nil {
// Try to load it again. // Try to load it again.
pbm_img, pbm_load_err := pbm.load(pbm_buf) pbm_img, pbm_load_err := pbm.load(pbm_buf)
defer pbm.destroy(pbm_img) defer pbm.destroy(pbm_img)
error = fmt.tprintf("%v test %v PBM load failed with %v.", file.file, count, pbm_load_err) testing.expectf(t, pbm_load_err == nil, "%v test %v PBM load failed with %v.", file.file, count, pbm_load_err)
expect(t, pbm_load_err == nil, error)
if pbm_load_err == nil { if pbm_load_err == nil {
pbm_hash := hash.crc32(pbm_img.pixels.buf[:]) pbm_hash := hash.crc32(pbm_img.pixels.buf[:])
testing.expectf(t, pbm_hash == png_hash, "%v test %v PBM load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, pbm_hash, png_hash, test.options)
error = fmt.tprintf("%v test %v PBM load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, pbm_hash, png_hash, test.options)
expect(t, pbm_hash == png_hash, error)
} }
} }
} }
@@ -1587,22 +1553,18 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
pbm_buf, pbm_save_err := pbm.save_to_buffer(img, pbm_info) pbm_buf, pbm_save_err := pbm.save_to_buffer(img, pbm_info)
defer delete(pbm_buf) defer delete(pbm_buf)
error = fmt.tprintf("%v test %v PBM save failed with %v.", file.file, count, pbm_save_err) testing.expectf(t, pbm_save_err == nil, "%v test %v PBM save failed with %v.", file.file, count, pbm_save_err)
expect(t, pbm_save_err == nil, error)
if pbm_save_err == nil { if pbm_save_err == nil {
// Try to load it again. // Try to load it again.
pbm_img, pbm_load_err := pbm.load(pbm_buf) pbm_img, pbm_load_err := pbm.load(pbm_buf)
defer pbm.destroy(pbm_img) defer pbm.destroy(pbm_img)
error = fmt.tprintf("%v test %v PBM load failed with %v.", file.file, count, pbm_load_err) testing.expectf(t, pbm_load_err == nil, "%v test %v PBM load failed with %v.", file.file, count, pbm_load_err)
expect(t, pbm_load_err == nil, error)
if pbm_load_err == nil { if pbm_load_err == nil {
pbm_hash := hash.crc32(pbm_img.pixels.buf[:]) pbm_hash := hash.crc32(pbm_img.pixels.buf[:])
testing.expectf(t, pbm_hash == png_hash, "%v test %v PBM load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, pbm_hash, png_hash, test.options)
error = fmt.tprintf("%v test %v PBM load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, pbm_hash, png_hash, test.options)
expect(t, pbm_hash == png_hash, error)
} }
} }
} }
@@ -1655,21 +1617,18 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
float_pbm_buf, float_pbm_save_err := pbm.save_to_buffer(float_img, pbm_info) float_pbm_buf, float_pbm_save_err := pbm.save_to_buffer(float_img, pbm_info)
defer delete(float_pbm_buf) defer delete(float_pbm_buf)
error = fmt.tprintf("%v test %v save as PFM failed with %v", file.file, count, float_pbm_save_err) testing.expectf(t, float_pbm_save_err == nil, "%v test %v save as PFM failed with %v", file.file, count, float_pbm_save_err)
expect(t, float_pbm_save_err == nil, error)
if float_pbm_save_err == nil { if float_pbm_save_err == nil {
// Load float image and compare. // Load float image and compare.
float_pbm_img, float_pbm_load_err := pbm.load(float_pbm_buf) float_pbm_img, float_pbm_load_err := pbm.load(float_pbm_buf)
defer pbm.destroy(float_pbm_img) defer pbm.destroy(float_pbm_img)
error = fmt.tprintf("%v test %v PFM load failed with %v", file.file, count, float_pbm_load_err) testing.expectf(t, float_pbm_load_err == nil, "%v test %v PFM load failed with %v", file.file, count, float_pbm_load_err)
expect(t, float_pbm_load_err == nil, error)
load_float := mem.slice_data_cast([]f32, float_pbm_img.pixels.buf[:]) load_float := mem.slice_data_cast([]f32, float_pbm_img.pixels.buf[:])
error = fmt.tprintf("%v test %v PFM load returned %v floats, expected %v", file.file, count, len(load_float), len(orig_float)) testing.expectf(t, len(load_float) == len(orig_float), "%v test %v PFM load returned %v floats, expected %v", file.file, count, len(load_float), len(orig_float))
expect(t, len(load_float) == len(orig_float), error)
// Compare floats // Compare floats
equal := true equal := true
@@ -1679,15 +1638,13 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
break break
} }
} }
error = fmt.tprintf("%v test %v PFM loaded floats to match", file.file, count) testing.expectf(t, equal, "%v test %v PFM loaded floats to match", file.file, count)
expect(t, equal, error)
} }
} }
} }
} }
if .return_metadata in test.options { if .return_metadata in test.options {
if v, ok := img.metadata.(^image.PNG_Info); ok { if v, ok := img.metadata.(^image.PNG_Info); ok {
for c in v.chunks { for c in v.chunks {
#partial switch(c.header.type) { #partial switch(c.header.type) {
@@ -1696,8 +1653,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
case "pp0n2c16", "pp0n6a08": case "pp0n2c16", "pp0n6a08":
gamma, gamma_ok := png.gamma(c) gamma, gamma_ok := png.gamma(c)
expected_gamma := f32(1.0) expected_gamma := f32(1.0)
error = fmt.tprintf("%v test %v gAMA is %v, expected %v.", file.file, count, gamma, expected_gamma) testing.expectf(t, gamma == expected_gamma && gamma_ok, "%v test %v gAMA is %v, expected %v.", file.file, count, gamma, expected_gamma)
expect(t, gamma == expected_gamma && gamma_ok, error)
} }
case .PLTE: case .PLTE:
switch(file.file) { switch(file.file) {
@@ -1705,8 +1661,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
plte, plte_ok := png.plte(c) plte, plte_ok := png.plte(c)
expected_plte_len := u16(216) expected_plte_len := u16(216)
error = fmt.tprintf("%v test %v PLTE length is %v, expected %v.", file.file, count, plte.used, expected_plte_len) testing.expectf(t, expected_plte_len == plte.used && plte_ok, "%v test %v PLTE length is %v, expected %v.", file.file, count, plte.used, expected_plte_len)
expect(t, expected_plte_len == plte.used && plte_ok, error)
} }
case .sPLT: case .sPLT:
switch(file.file) { switch(file.file) {
@@ -1714,12 +1669,10 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
splt, splt_ok := png.splt(c) splt, splt_ok := png.splt(c)
expected_splt_len := u16(216) expected_splt_len := u16(216)
error = fmt.tprintf("%v test %v sPLT length is %v, expected %v.", file.file, count, splt.used, expected_splt_len) testing.expectf(t, expected_splt_len == splt.used && splt_ok, "%v test %v sPLT length is %v, expected %v.", file.file, count, splt.used, expected_splt_len)
expect(t, expected_splt_len == splt.used && splt_ok, error)
expected_splt_name := "six-cube" expected_splt_name := "six-cube"
error = fmt.tprintf("%v test %v sPLT name is %v, expected %v.", file.file, count, splt.name, expected_splt_name) testing.expectf(t, expected_splt_name == splt.name && splt_ok, "%v test %v sPLT name is %v, expected %v.", file.file, count, splt.name, expected_splt_name)
expect(t, expected_splt_name == splt.name && splt_ok, error)
png.splt_destroy(splt) png.splt_destroy(splt)
} }
@@ -1733,48 +1686,37 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
g = png.CIE_1931{x = 0.3000, y = 0.6000}, g = png.CIE_1931{x = 0.3000, y = 0.6000},
b = png.CIE_1931{x = 0.1500, y = 0.0600}, b = png.CIE_1931{x = 0.1500, y = 0.0600},
} }
error = fmt.tprintf("%v test %v cHRM is %v, expected %v.", file.file, count, chrm, expected_chrm) testing.expectf(t, expected_chrm == chrm && chrm_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, chrm, expected_chrm)
expect(t, expected_chrm == chrm && chrm_ok, error)
} }
case .pHYs: case .pHYs:
phys, phys_ok := png.phys(c) phys, phys_ok := png.phys(c)
phys_err := "%v test %v cHRM is %v, expected %v."
switch (file.file) { switch (file.file) {
case "cdfn2c08": case "cdfn2c08":
expected_phys := png.pHYs{ppu_x = 1, ppu_y = 4, unit = .Unknown} expected_phys := png.pHYs{ppu_x = 1, ppu_y = 4, unit = .Unknown}
error = fmt.tprintf(phys_err, file.file, count, phys, expected_phys) testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, phys, expected_phys)
expect(t, expected_phys == phys && phys_ok, error)
case "cdhn2c08": case "cdhn2c08":
expected_phys := png.pHYs{ppu_x = 4, ppu_y = 1, unit = .Unknown} expected_phys := png.pHYs{ppu_x = 4, ppu_y = 1, unit = .Unknown}
error = fmt.tprintf(phys_err, file.file, count, phys, expected_phys) testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, phys, expected_phys)
expect(t, expected_phys == phys && phys_ok, error)
case "cdsn2c08": case "cdsn2c08":
expected_phys := png.pHYs{ppu_x = 1, ppu_y = 1, unit = .Unknown} expected_phys := png.pHYs{ppu_x = 1, ppu_y = 1, unit = .Unknown}
error = fmt.tprintf(phys_err, file.file, count, phys, expected_phys) testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, phys, expected_phys)
expect(t, expected_phys == phys && phys_ok, error)
case "cdun2c08": case "cdun2c08":
expected_phys := png.pHYs{ppu_x = 1000, ppu_y = 1000, unit = .Meter} expected_phys := png.pHYs{ppu_x = 1000, ppu_y = 1000, unit = .Meter}
error = fmt.tprintf(phys_err, file.file, count, phys, expected_phys) testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, phys, expected_phys)
expect(t, expected_phys == phys && phys_ok, error)
} }
case .hIST: case .hIST:
hist, hist_ok := png.hist(c) hist, hist_ok := png.hist(c)
hist_err := "%v test %v hIST has %v entries, expected %v."
switch (file.file) { switch (file.file) {
case "ch1n3p04": case "ch1n3p04":
error = fmt.tprintf(hist_err, file.file, count, hist.used, 15) testing.expectf(t, hist.used == 15 && hist_ok, "%v test %v hIST has %v entries, expected %v.", file.file, count, hist.used, 15)
expect(t, hist.used == 15 && hist_ok, error)
case "ch2n3p08": case "ch2n3p08":
error = fmt.tprintf(hist_err, file.file, count, hist.used, 256) testing.expectf(t, hist.used == 256 && hist_ok, "%v test %v hIST has %v entries, expected %v.", file.file, count, hist.used, 256)
expect(t, hist.used == 256 && hist_ok, error)
} }
case .tIME: case .tIME:
png_time, png_time_ok := png.time(c) png_time, png_time_ok := png.time(c)
time_err := "%v test %v tIME was %v, expected %v."
expected_time: png.tIME expected_time: png.tIME
core_time, core_time_ok := png.core_time(c) core_time, core_time_ok := png.core_time(c)
time_core_err := "%v test %v tIME->core:time is %v, expected %v."
expected_core: time.Time expected_core: time.Time
switch(file.file) { switch(file.file) {
@@ -1789,14 +1731,10 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
expected_core = time.Time{_nsec = 946684799000000000} expected_core = time.Time{_nsec = 946684799000000000}
} }
error = fmt.tprintf(time_err, file.file, count, png_time, expected_time) testing.expectf(t, png_time == expected_time && png_time_ok, "%v test %v tIME was %v, expected %v.", file.file, count, png_time, expected_time)
expect(t, png_time == expected_time && png_time_ok, error) testing.expectf(t, core_time == expected_core && core_time_ok, "%v test %v tIME->core:time is %v, expected %v.", file.file, count, core_time, expected_core)
error = fmt.tprintf(time_core_err, file.file, count, core_time, expected_core)
expect(t, core_time == expected_core && core_time_ok, error)
case .sBIT: case .sBIT:
sbit, sbit_ok := png.sbit(c) sbit, sbit_ok := png.sbit(c)
sbit_err := "%v test %v sBIT was %v, expected %v."
expected_sbit: [4]u8 expected_sbit: [4]u8
switch (file.file) { switch (file.file) {
@@ -1815,8 +1753,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
case "cdfn2c08", "cdhn2c08", "cdsn2c08", "cdun2c08", "ch1n3p04", "basn3p04": case "cdfn2c08", "cdhn2c08", "cdsn2c08", "cdun2c08", "ch1n3p04", "basn3p04":
expected_sbit = [4]u8{ 4, 4, 4, 0} expected_sbit = [4]u8{ 4, 4, 4, 0}
} }
error = fmt.tprintf(sbit_err, file.file, count, sbit, expected_sbit) testing.expectf(t, sbit == expected_sbit && sbit_ok, "%v test %v sBIT was %v, expected %v.", file.file, count, sbit, expected_sbit)
expect(t, sbit == expected_sbit && sbit_ok, error)
case .tEXt, .zTXt: case .tEXt, .zTXt:
text, text_ok := png.text(c) text, text_ok := png.text(c)
defer png.text_destroy(text) defer png.text_destroy(text)
@@ -1828,8 +1765,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
if file.file in Expected_Text { if file.file in Expected_Text {
if text.keyword in Expected_Text[file.file] { if text.keyword in Expected_Text[file.file] {
test_text := Expected_Text[file.file][text.keyword].text test_text := Expected_Text[file.file][text.keyword].text
error = fmt.tprintf("%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text.text, test_text) testing.expectf(t, text.text == test_text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text.text, test_text)
expect(t, text.text == test_text && text_ok, error)
} }
} }
} }
@@ -1842,74 +1778,59 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
if file.file in Expected_Text { if file.file in Expected_Text {
if text.keyword in Expected_Text[file.file] { if text.keyword in Expected_Text[file.file] {
test := Expected_Text[file.file][text.keyword] test := Expected_Text[file.file][text.keyword]
error = fmt.tprintf("%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test) testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
expect(t, text.language == test.language && text_ok, error) testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
expect(t, text.keyword_localized == test.keyword_localized && text_ok, error)
} }
} }
case "ctfn0g04": // international UTF-8, finnish case "ctfn0g04": // international UTF-8, finnish
if file.file in Expected_Text { if file.file in Expected_Text {
if text.keyword in Expected_Text[file.file] { if text.keyword in Expected_Text[file.file] {
test := Expected_Text[file.file][text.keyword] test := Expected_Text[file.file][text.keyword]
error = fmt.tprintf("%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test) testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
expect(t, text.text == test.text && text_ok, error) testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
expect(t, text.language == test.language && text_ok, error) testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
expect(t, text.keyword_localized == test.keyword_localized && text_ok, error)
} }
} }
case "ctgn0g04": // international UTF-8, greek case "ctgn0g04": // international UTF-8, greek
if file.file in Expected_Text { if file.file in Expected_Text {
if text.keyword in Expected_Text[file.file] { if text.keyword in Expected_Text[file.file] {
test := Expected_Text[file.file][text.keyword] test := Expected_Text[file.file][text.keyword]
error = fmt.tprintf("%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test) testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
expect(t, text.text == test.text && text_ok, error) testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
expect(t, text.language == test.language && text_ok, error) testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
expect(t, text.keyword_localized == test.keyword_localized && text_ok, error)
} }
} }
case "cthn0g04": // international UTF-8, hindi case "cthn0g04": // international UTF-8, hindi
if file.file in Expected_Text { if file.file in Expected_Text {
if text.keyword in Expected_Text[file.file] { if text.keyword in Expected_Text[file.file] {
test := Expected_Text[file.file][text.keyword] test := Expected_Text[file.file][text.keyword]
error = fmt.tprintf("%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test) testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
expect(t, text.text == test.text && text_ok, error) testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
expect(t, text.language == test.language && text_ok, error) testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
expect(t, text.keyword_localized == test.keyword_localized && text_ok, error)
} }
} }
case "ctjn0g04": // international UTF-8, japanese case "ctjn0g04": // international UTF-8, japanese
if file.file in Expected_Text { if file.file in Expected_Text {
if text.keyword in Expected_Text[file.file] { if text.keyword in Expected_Text[file.file] {
test := Expected_Text[file.file][text.keyword] test := Expected_Text[file.file][text.keyword]
error = fmt.tprintf("%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test) testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
expect(t, text.text == test.text && text_ok, error) testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
expect(t, text.language == test.language && text_ok, error) testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
expect(t, text.keyword_localized == test.keyword_localized && text_ok, error)
} }
} }
} }
case .eXIf: case .eXIf:
if file.file == "exif2c08" { // chunk with jpeg exif data if file.file == "exif2c08" { // chunk with jpeg exif data
exif, exif_ok := png.exif(c) exif, exif_ok := png.exif(c)
error = fmt.tprintf("%v test %v eXIf byte order '%v', expected 'big_endian'.", file.file, count, exif.byte_order) testing.expectf(t, exif.byte_order == .big_endian && exif_ok, "%v test %v eXIf byte order '%v', expected 'big_endian'.", file.file, count, exif.byte_order)
error_len := fmt.tprintf("%v test %v eXIf data length '%v', expected '%v'.", file.file, len(exif.data), 978) testing.expectf(t, len(exif.data) == 978 && exif_ok, "%v test %v eXIf data length '%v', expected '%v'.", file.file, len(exif.data), 978)
expect(t, exif.byte_order == .big_endian && exif_ok, error)
expect(t, len(exif.data) == 978 && exif_ok, error_len)
} }
} }
} }
} }
} }
} }
png.destroy(img) png.destroy(img)
for _, v in track.allocation_map {
error = fmt.tprintf("%v test %v leaked %v bytes @ loc %v.", file.file, count, v.size, v.location)
expect(t, false, error)
}
} }
} }
}
return
}
+1 -1
View File
@@ -5,7 +5,7 @@ set TEST_ARGS=-fast-tests
set TEST_ARGS=-no-random set TEST_ARGS=-no-random
set TEST_ARGS= set TEST_ARGS=
set OUT_NAME=math_big_test_library.dll set OUT_NAME=math_big_test_library.dll
set COMMON=-build-mode:shared -show-timings -no-bounds-check -define:MATH_BIG_EXE=false -vet -strict-style set COMMON=-build-mode:shared -show-timings -no-bounds-check -define:MATH_BIG_EXE=false -vet -strict-style -define:ODIN_TEST_FANCY=false
echo --- echo ---
echo Running core:math/big tests echo Running core:math/big tests
echo --- echo ---
@@ -1,24 +1,10 @@
// Tests "linalg_glsl_math.odin" in "core:math/linalg/glsl". // Tests "linalg_glsl_math.odin" in "core:math/linalg/glsl".
// Must be run with `-collection:tests=` flag, e.g.
// ./odin run tests/core/math/linalg/glsl/test_linalg_glsl_math.odin -collection:tests=./tests
package test_core_math_linalg_glsl_math package test_core_math_linalg_glsl_math
import glsl "core:math/linalg/glsl" import glsl "core:math/linalg/glsl"
import "core:fmt"
import "core:math" import "core:math"
import "core:testing" import "core:testing"
import tc "tests:common"
main :: proc() {
t := testing.T{}
test_fract_f32(&t)
test_fract_f64(&t)
tc.report(&t)
}
@test @test
test_fract_f32 :: proc(t: ^testing.T) { test_fract_f32 :: proc(t: ^testing.T) {
@@ -45,7 +31,7 @@ test_fract_f32 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r = glsl.fract(d.v) r = glsl.fract(d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%v (%h)) -> %v (%h) != %v", i, #procedure, d.v, d.v, r, r, d.e)) testing.expectf(t, r == d.e, "%v (%h) -> %v (%h) != %v", d.v, d.v, r, r, d.e)
} }
} }
@@ -74,6 +60,6 @@ test_fract_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r = glsl.fract(d.v) r = glsl.fract(d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%v (%h)) -> %v (%h) != %v", i, #procedure, d.v, d.v, r, r, d.e)) testing.expectf(t, r == d.e, "%v (%h) -> %v (%h) != %v", d.v, d.v, r, r, d.e)
} }
} }
+74 -121
View File
@@ -2,42 +2,6 @@ package test_core_math_noise
import "core:testing" import "core:testing"
import "core:math/noise" import "core:math/noise"
import "core:fmt"
import "core:os"
TEST_count := 0
TEST_fail := 0
V2 :: noise.Vec2
V3 :: noise.Vec3
V4 :: noise.Vec4
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
main :: proc() {
t := testing.T{}
noise_test(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
Test_Vector :: struct { Test_Vector :: struct {
seed: i64, seed: i64,
@@ -51,6 +15,10 @@ Test_Vector :: struct {
}, },
} }
V2 :: noise.Vec2
V3 :: noise.Vec3
V4 :: noise.Vec4
SEED_1 :: 2324223232 SEED_1 :: 2324223232
SEED_2 :: 932466901 SEED_2 :: 932466901
SEED_3 :: 9321 SEED_3 :: 9321
@@ -59,93 +27,78 @@ COORD_1 :: V4{ 242.0, 3433.0, 920.0, 222312.0}
COORD_2 :: V4{ 590.0, 9411.0, 5201.0, 942124256.0} COORD_2 :: V4{ 590.0, 9411.0, 5201.0, 942124256.0}
COORD_3 :: V4{12090.0, 19411.0, 81950901.0, 4224219.0} COORD_3 :: V4{12090.0, 19411.0, 81950901.0, 4224219.0}
Noise_Tests := []Test_Vector{ @(test)
/* test_noise_2d :: proc(t: ^testing.T) {
`noise_2d` tests. test(t, {SEED_1, COORD_1.xy, 0.25010583, noise.noise_2d})
*/ test(t, {SEED_2, COORD_2.xy, -0.92513955, noise.noise_2d})
{SEED_1, COORD_1.xy, 0.25010583, noise.noise_2d}, test(t, {SEED_3, COORD_3.xy, 0.67327416, noise.noise_2d})
{SEED_2, COORD_2.xy, -0.92513955, noise.noise_2d},
{SEED_3, COORD_3.xy, 0.67327416, noise.noise_2d},
/*
`noise_2d_improve_x` tests.
*/
{SEED_1, COORD_1.xy, 0.17074019, noise.noise_2d_improve_x},
{SEED_2, COORD_2.xy, 0.72330487, noise.noise_2d_improve_x},
{SEED_3, COORD_3.xy, -0.032076947, noise.noise_2d_improve_x},
/*
`noise_3d_improve_xy` tests.
*/
{SEED_1, COORD_1.xyz, 0.14819577, noise.noise_3d_improve_xy},
{SEED_2, COORD_2.xyz, -0.065345764, noise.noise_3d_improve_xy},
{SEED_3, COORD_3.xyz, -0.37761918, noise.noise_3d_improve_xy},
/*
`noise_3d_improve_xz` tests.
*/
{SEED_1, COORD_1.xyz, -0.50075006, noise.noise_3d_improve_xz},
{SEED_2, COORD_2.xyz, -0.36039603, noise.noise_3d_improve_xz},
{SEED_3, COORD_3.xyz, -0.3479203, noise.noise_3d_improve_xz},
/*
`noise_3d_fallback` tests.
*/
{SEED_1, COORD_1.xyz, 0.6557345, noise.noise_3d_fallback},
{SEED_2, COORD_2.xyz, 0.55452216, noise.noise_3d_fallback},
{SEED_3, COORD_3.xyz, -0.26408964, noise.noise_3d_fallback},
/*
`noise_3d_fallback` tests.
*/
{SEED_1, COORD_1.xyz, 0.6557345, noise.noise_3d_fallback},
{SEED_2, COORD_2.xyz, 0.55452216, noise.noise_3d_fallback},
{SEED_3, COORD_3.xyz, -0.26408964, noise.noise_3d_fallback},
/*
`noise_4d_improve_xyz_improve_xy` tests.
*/
{SEED_1, COORD_1, 0.44929826, noise.noise_4d_improve_xyz_improve_xy},
{SEED_2, COORD_2, -0.13270882, noise.noise_4d_improve_xyz_improve_xy},
{SEED_3, COORD_3, 0.10298563, noise.noise_4d_improve_xyz_improve_xy},
/*
`noise_4d_improve_xyz_improve_xz` tests.
*/
{SEED_1, COORD_1, -0.078514606, noise.noise_4d_improve_xyz_improve_xz},
{SEED_2, COORD_2, -0.032157656, noise.noise_4d_improve_xyz_improve_xz},
{SEED_3, COORD_3, -0.38607058, noise.noise_4d_improve_xyz_improve_xz},
/*
`noise_4d_improve_xyz` tests.
*/
{SEED_1, COORD_1, -0.4442258, noise.noise_4d_improve_xyz},
{SEED_2, COORD_2, 0.36822623, noise.noise_4d_improve_xyz},
{SEED_3, COORD_3, 0.22628775, noise.noise_4d_improve_xyz},
/*
`noise_4d_fallback` tests.
*/
{SEED_1, COORD_1, -0.14233987, noise.noise_4d_fallback},
{SEED_2, COORD_2, 0.1354035, noise.noise_4d_fallback},
{SEED_3, COORD_3, 0.14565045, noise.noise_4d_fallback},
} }
noise_test :: proc(t: ^testing.T) { @(test)
for test in Noise_Tests { test_noise_2d_improve_x :: proc(t: ^testing.T) {
output: f32 test(t, {SEED_1, COORD_1.xy, 0.17074019, noise.noise_2d_improve_x})
test(t, {SEED_2, COORD_2.xy, 0.72330487, noise.noise_2d_improve_x})
test(t, {SEED_3, COORD_3.xy, -0.032076947, noise.noise_2d_improve_x})
}
switch coord in test.coord { @(test)
case V2: test_noise_3d_improve_xy :: proc(t: ^testing.T) {
output = test.test_proc.(proc(_: i64, _: V2) -> f32)(test.seed, test.coord.(V2)) test(t, {SEED_1, COORD_1.xyz, 0.14819577, noise.noise_3d_improve_xy})
case V3: test(t, {SEED_2, COORD_2.xyz, -0.065345764, noise.noise_3d_improve_xy})
output = test.test_proc.(proc(_: i64, _: V3) -> f32)(test.seed, test.coord.(V3)) test(t, {SEED_3, COORD_3.xyz, -0.37761918, noise.noise_3d_improve_xy})
case V4: }
output = test.test_proc.(proc(_: i64, _: V4) -> f32)(test.seed, test.coord.(V4))
} @(test)
test_noise_3d_improve_xz :: proc(t: ^testing.T) {
error := fmt.tprintf("Seed %v, Coord: %v, Expected: %3.8f. Got %3.8f", test.seed, test.coord, test.expected, output) test(t, {SEED_1, COORD_1.xyz, -0.50075006, noise.noise_3d_improve_xz})
expect(t, test.expected == output, error) test(t, {SEED_2, COORD_2.xyz, -0.36039603, noise.noise_3d_improve_xz})
test(t, {SEED_3, COORD_3.xyz, -0.3479203, noise.noise_3d_improve_xz})
}
@(test)
test_noise_3d_fallback :: proc(t: ^testing.T) {
test(t, {SEED_1, COORD_1.xyz, 0.6557345, noise.noise_3d_fallback})
test(t, {SEED_2, COORD_2.xyz, 0.55452216, noise.noise_3d_fallback})
test(t, {SEED_3, COORD_3.xyz, -0.26408964, noise.noise_3d_fallback})
}
@(test)
test_noise_4d_improve_xyz_improve_xy :: proc(t: ^testing.T) {
test(t, {SEED_1, COORD_1, 0.44929826, noise.noise_4d_improve_xyz_improve_xy})
test(t, {SEED_2, COORD_2, -0.13270882, noise.noise_4d_improve_xyz_improve_xy})
test(t, {SEED_3, COORD_3, 0.10298563, noise.noise_4d_improve_xyz_improve_xy})
}
@(test)
test_noise_4d_improve_xyz_improve_xz :: proc(t: ^testing.T) {
test(t, {SEED_1, COORD_1, -0.078514606, noise.noise_4d_improve_xyz_improve_xz})
test(t, {SEED_2, COORD_2, -0.032157656, noise.noise_4d_improve_xyz_improve_xz})
test(t, {SEED_3, COORD_3, -0.38607058, noise.noise_4d_improve_xyz_improve_xz})
}
@(test)
test_noise_4d_improve_xyz :: proc(t: ^testing.T) {
test(t, {SEED_1, COORD_1, -0.4442258, noise.noise_4d_improve_xyz})
test(t, {SEED_2, COORD_2, 0.36822623, noise.noise_4d_improve_xyz})
test(t, {SEED_3, COORD_3, 0.22628775, noise.noise_4d_improve_xyz})
}
@(test)
test_noise_4d_fallback :: proc(t: ^testing.T) {
test(t, {SEED_1, COORD_1, -0.14233987, noise.noise_4d_fallback})
test(t, {SEED_2, COORD_2, 0.1354035, noise.noise_4d_fallback})
test(t, {SEED_3, COORD_3, 0.14565045, noise.noise_4d_fallback})
}
test :: proc(t: ^testing.T, test: Test_Vector) {
output: f32
switch coord in test.coord {
case V2:
output = test.test_proc.(proc(_: i64, _: V2) -> f32)(test.seed, test.coord.(V2))
case V3:
output = test.test_proc.(proc(_: i64, _: V3) -> f32)(test.seed, test.coord.(V3))
case V4:
output = test.test_proc.(proc(_: i64, _: V4) -> f32)(test.seed, test.coord.(V4))
} }
testing.expectf(t, test.expected == output, "Seed %v, Coord: %v, Expected: %3.8f. Got %3.8f", test.seed, test.coord, test.expected, output)
} }
+93 -165
View File
@@ -1,49 +1,8 @@
// Tests "math.odin" in "core:math". // Tests "math.odin" in "core:math".
// Must be run with `-collection:tests=` flag, e.g.
// ./odin run tests/core/math/test_core_math.odin -collection:tests=./tests
package test_core_math package test_core_math
import "core:fmt"
import "core:math" import "core:math"
import "core:testing" import "core:testing"
import tc "tests:common"
main :: proc() {
t := testing.T{}
test_classify_f16(&t)
test_classify_f32(&t)
test_classify_f64(&t)
test_trunc_f16(&t)
test_trunc_f32(&t)
test_trunc_f64(&t)
test_round_f16(&t)
test_round_f32(&t)
test_round_f64(&t)
test_nan(&t)
test_acos(&t)
test_acosh(&t)
test_asin(&t)
test_asinh(&t)
test_atan(&t)
test_atanh(&t)
test_atan2(&t)
test_cos(&t)
test_cosh(&t)
test_sin(&t)
test_sinh(&t)
test_sqrt(&t)
test_tan(&t)
test_tanh(&t)
test_large_cos(&t)
test_large_sin(&t)
test_large_tan(&t)
tc.report(&t)
}
@test @test
test_classify_f16 :: proc(t: ^testing.T) { test_classify_f16 :: proc(t: ^testing.T) {
@@ -68,7 +27,7 @@ test_classify_f16 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r = math.classify_f16(d.v) r = math.classify_f16(d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e)) testing.expectf(t, r == d.e, "%h -> %v != %v", d.v, r, d.e)
} }
/* Check all subnormals (exponent 0, 10-bit significand non-zero) */ /* Check all subnormals (exponent 0, 10-bit significand non-zero) */
@@ -76,7 +35,7 @@ test_classify_f16 :: proc(t: ^testing.T) {
v := transmute(f16)i v := transmute(f16)i
r = math.classify_f16(v) r = math.classify_f16(v)
e :: math.Float_Class.Subnormal e :: math.Float_Class.Subnormal
tc.expect(t, r == e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, v, r, e)) testing.expectf(t, r == e, "%h -> %v != %v", v, r, e)
} }
} }
@@ -103,7 +62,7 @@ test_classify_f32 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r = math.classify_f32(d.v) r = math.classify_f32(d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e)) testing.expectf(t, r == d.e, "%h -> %v != %v", d.v, r, d.e)
} }
} }
@@ -130,7 +89,7 @@ test_classify_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r = math.classify_f64(d.v) r = math.classify_f64(d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e)) testing.expectf(t, r == d.e, "%h -> %v != %v", d.v, r, d.e)
} }
} }
@@ -175,16 +134,16 @@ test_trunc_f16 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r = math.trunc_f16(d.v) r = math.trunc_f16(d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e)) testing.expectf(t, r == d.e, "%h -> %h != %h", d.v, r, d.e)
} }
v = math.SNAN_F16 v = math.SNAN_F16
r = math.trunc_f16(v) r = math.trunc_f16(v)
tc.expect(t, math.is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) testing.expectf(t, math.is_nan_f16(r), "%f != NaN", v, r)
v = math.QNAN_F16 v = math.QNAN_F16
r = math.trunc_f16(v) r = math.trunc_f16(v)
tc.expect(t, math.is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) testing.expectf(t, math.is_nan_f16(r), "%f != NaN", v, r)
} }
@test @test
@@ -237,16 +196,16 @@ test_trunc_f32 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r = math.trunc_f32(d.v) r = math.trunc_f32(d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e)) testing.expectf(t, r == d.e, "%h -> %h != %h", d.v, r, d.e)
} }
v = math.SNAN_F32 v = math.SNAN_F32
r = math.trunc_f32(v) r = math.trunc_f32(v)
tc.expect(t, math.is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) testing.expectf(t, math.is_nan_f32(r), "%f -> %f != NaN", v, r)
v = math.QNAN_F32 v = math.QNAN_F32
r = math.trunc_f32(v) r = math.trunc_f32(v)
tc.expect(t, math.is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) testing.expectf(t, math.is_nan_f32(r), "%f -> %f != NaN", v, r)
} }
@test @test
@@ -299,16 +258,16 @@ test_trunc_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r = math.trunc_f64(d.v) r = math.trunc_f64(d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e)) testing.expectf(t, r == d.e, "%h -> %h != %h", d.v, r, d.e)
} }
v = math.SNAN_F64 v = math.SNAN_F64
r = math.trunc_f64(v) r = math.trunc_f64(v)
tc.expect(t, math.is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) testing.expectf(t, math.is_nan_f64(r), "%f -> %f != NaN", v, r)
v = math.QNAN_F64 v = math.QNAN_F64
r = math.trunc_f64(v) r = math.trunc_f64(v)
tc.expect(t, math.is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) testing.expectf(t, math.is_nan_f64(r), "%f -> %f != NaN", v, r)
} }
@test @test
@@ -352,16 +311,16 @@ test_round_f16 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r = math.round_f16(d.v) r = math.round_f16(d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e)) testing.expectf(t, r == d.e, "%h -> %h != %h", d.v, r, d.e)
} }
v = math.SNAN_F16 v = math.SNAN_F16
r = math.round_f16(v) r = math.round_f16(v)
tc.expect(t, math.is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) testing.expectf(t, math.is_nan_f16(r), "%f -> %f != NaN", v, r)
v = math.QNAN_F16 v = math.QNAN_F16
r = math.round_f16(v) r = math.round_f16(v)
tc.expect(t, math.is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) testing.expectf(t, math.is_nan_f16(r), "%f -> %f != NaN", v, r)
} }
@test @test
@@ -414,16 +373,16 @@ test_round_f32 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r = math.round_f32(d.v) r = math.round_f32(d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e)) testing.expectf(t, r == d.e, "%h -> %h != %h", i, d.v, r, d.e)
} }
v = math.SNAN_F32 v = math.SNAN_F32
r = math.round_f32(v) r = math.round_f32(v)
tc.expect(t, math.is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) testing.expectf(t, math.is_nan_f32(r), "%f -> %f != NaN", v, r)
v = math.QNAN_F32 v = math.QNAN_F32
r = math.round_f32(v) r = math.round_f32(v)
tc.expect(t, math.is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) testing.expectf(t, math.is_nan_f32(r), "%f -> %f != NaN", v, r)
} }
@test @test
@@ -476,16 +435,16 @@ test_round_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r = math.round_f64(d.v) r = math.round_f64(d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e)) testing.expectf(t, r == d.e, "%h -> %h != %h", d.v, r, d.e)
} }
v = math.SNAN_F64 v = math.SNAN_F64
r = math.round_f64(v) r = math.round_f64(v)
tc.expect(t, math.is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) testing.expectf(t, math.is_nan_f64(r), "%f -> %f != NaN", v, r)
v = math.QNAN_F64 v = math.QNAN_F64
r = math.round_f64(v) r = math.round_f64(v)
tc.expect(t, math.is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) testing.expectf(t, math.is_nan_f64(r), "%f -> %f != NaN", v, r)
} }
@@ -1033,17 +992,17 @@ tolerance :: proc(a, b, e: f64) -> bool {
} }
close :: proc(t: ^testing.T, a, b: f64, loc := #caller_location) -> bool { close :: proc(t: ^testing.T, a, b: f64, loc := #caller_location) -> bool {
ok := tolerance(a, b, 1e-9) ok := tolerance(a, b, 1e-9)
// tc.expect(t, ok, fmt.tprintf("%.15g is not close to %.15g", a, b), loc) testing.expectf(t, ok, "%.15g is not close to %.15g", a, b, loc=loc)
return ok return ok
} }
veryclose :: proc(t: ^testing.T, a, b: f64, loc := #caller_location) -> bool { veryclose :: proc(t: ^testing.T, a, b: f64, loc := #caller_location) -> bool {
ok := tolerance(a, b, 4e-14) ok := tolerance(a, b, 4e-14)
// tc.expect(t, ok, fmt.tprintf("%.15g is not veryclose to %.15g", a, b), loc) testing.expectf(t, ok, "%.15g is not veryclose to %.15g", a, b, loc=loc)
return ok return ok
} }
soclose :: proc(t: ^testing.T, a, b, e: f64, loc := #caller_location) -> bool { soclose :: proc(t: ^testing.T, a, b, e: f64, loc := #caller_location) -> bool {
ok := tolerance(a, b, e) ok := tolerance(a, b, e)
// tc.expect(t, ok, fmt.tprintf("%.15g is not soclose to %.15g", a, b), loc) testing.expectf(t, ok, "%.15g is not soclose to %.15g", a, b, loc=loc)
return ok return ok
} }
alike :: proc(t: ^testing.T, a, b: f64, loc := #caller_location) -> bool { alike :: proc(t: ^testing.T, a, b: f64, loc := #caller_location) -> bool {
@@ -1054,34 +1013,34 @@ alike :: proc(t: ^testing.T, a, b: f64, loc := #caller_location) -> bool {
case a == b: case a == b:
ok = math.signbit(a) == math.signbit(b) ok = math.signbit(a) == math.signbit(b)
} }
// tc.expect(t, ok, fmt.tprintf("%.15g is not alike to %.15g", a, b), loc) testing.expectf(t, ok, "%.15g is not alike to %.15g", a, b, loc=loc)
return ok return ok
} }
@test @test
test_nan :: proc(t: ^testing.T) { test_nan32 :: proc(t: ^testing.T) {
float32 := f32(NaN)
equal := float32 == float32
testing.expectf(t, !equal, "float32(NaN) is %.15g, expected NaN", float32)
}
@test
test_nan64 :: proc(t: ^testing.T) {
float64 := NaN float64 := NaN
if float64 == float64 { equal := float64 == float64
tc.errorf(t, "NaN returns %.15g, expected NaN", float64) testing.expectf(t, !equal, "NaN returns %.15g, expected NaN", float64)
}
float32 := f32(float64)
if float32 == float32 {
tc.errorf(t, "float32(NaN) is %.15g, expected NaN", float32)
}
} }
@test @test
test_acos :: proc(t: ^testing.T) { test_acos :: proc(t: ^testing.T) {
for _, i in vf { for _, i in vf {
a := vf[i] / 10 a := vf[i] / 10
if f := math.acos(a); !close(t, acos[i], f) { f := math.acos(a)
tc.errorf(t, "math.acos(%.15g) = %.15g, want %.15g", a, f, acos[i]) testing.expectf(t, close(t, acos[i], f), "math.acos(%.15g) = %.15g, want %.15g", a, f, acos[i])
}
} }
for _, i in vfacos_sc { for _, i in vfacos_sc {
if f := math.acos(vfacos_sc[i]); !alike(t, acos_sc[i], f) { f := math.acos(vfacos_sc[i])
tc.errorf(t, "math.acos(%.15g) = %.15g, want %.15g", vfacos_sc[i], f, acos_sc[i]) testing.expectf(t, alike(t, acos_sc[i], f), "math.acos(%.15g) = %.15g, want %.15g", vfacos_sc[i], f, acos_sc[i])
}
} }
} }
@@ -1089,14 +1048,12 @@ test_acos :: proc(t: ^testing.T) {
test_acosh :: proc(t: ^testing.T) { test_acosh :: proc(t: ^testing.T) {
for _, i in vf { for _, i in vf {
a := 1 + abs(vf[i]) a := 1 + abs(vf[i])
if f := math.acosh(a); !veryclose(t, acosh[i], f) { f := math.acosh(a)
tc.errorf(t, "math.acosh(%.15g) = %.15g, want %.15g", a, f, acosh[i]) testing.expectf(t, veryclose(t, acosh[i], f), "math.acosh(%.15g) = %.15g, want %.15g", a, f, acosh[i])
}
} }
for _, i in vfacosh_sc { for _, i in vfacosh_sc {
if f := math.acosh(vfacosh_sc[i]); !alike(t, acosh_sc[i], f) { f := math.acosh(vfacosh_sc[i])
tc.errorf(t, "math.acosh(%.15g) = %.15g, want %.15g", vfacosh_sc[i], f, acosh_sc[i]) testing.expectf(t, alike(t, acosh_sc[i], f), "math.acosh(%.15g) = %.15g, want %.15g", vfacosh_sc[i], f, acosh_sc[i])
}
} }
} }
@@ -1104,42 +1061,36 @@ test_acosh :: proc(t: ^testing.T) {
test_asin :: proc(t: ^testing.T) { test_asin :: proc(t: ^testing.T) {
for _, i in vf { for _, i in vf {
a := vf[i] / 10 a := vf[i] / 10
if f := math.asin(a); !veryclose(t, asin[i], f) { f := math.asin(a)
tc.errorf(t, "math.asin(%.15g) = %.15g, want %.15g", a, f, asin[i]) testing.expectf(t, veryclose(t, asin[i], f), "math.asin(%.15g) = %.15g, want %.15g", a, f, asin[i])
}
} }
for _, i in vfasin_sc { for _, i in vfasin_sc {
if f := math.asin(vfasin_sc[i]); !alike(t, asin_sc[i], f) { f := math.asin(vfasin_sc[i])
tc.errorf(t, "math.asin(%.15g) = %.15g, want %.15g", vfasin_sc[i], f, asin_sc[i]) testing.expectf(t, alike(t, asin_sc[i], f), "math.asin(%.15g) = %.15g, want %.15g", vfasin_sc[i], f, asin_sc[i])
}
} }
} }
@test @test
test_asinh :: proc(t: ^testing.T) { test_asinh :: proc(t: ^testing.T) {
for _, i in vf { for _, i in vf {
if f := math.asinh(vf[i]); !veryclose(t, asinh[i], f) { f := math.asinh(vf[i])
tc.errorf(t, "math.asinh(%.15g) = %.15g, want %.15g", vf[i], f, asinh[i]) testing.expectf(t, veryclose(t, asinh[i], f), "math.asinh(%.15g) = %.15g, want %.15g", vf[i], f, asinh[i])
}
} }
for _, i in vfasinh_sc { for _, i in vfasinh_sc {
if f := math.asinh(vfasinh_sc[i]); !alike(t, asinh_sc[i], f) { f := math.asinh(vfasinh_sc[i])
tc.errorf(t, "math.asinh(%.15g) = %.15g, want %.15g", vfasinh_sc[i], f, asinh_sc[i]) testing.expectf(t, alike(t, asinh_sc[i], f), "math.asinh(%.15g) = %.15g, want %.15g", vfasinh_sc[i], f, asinh_sc[i])
}
} }
} }
@test @test
test_atan :: proc(t: ^testing.T) { test_atan :: proc(t: ^testing.T) {
for _, i in vf { for _, i in vf {
if f := math.atan(vf[i]); !veryclose(t, atan[i], f) { f := math.atan(vf[i])
tc.errorf(t, "math.atan(%.15g) = %.15g, want %.15g", vf[i], f, atan[i]) testing.expectf(t, veryclose(t, atan[i], f), "math.atan(%.15g) = %.15g, want %.15g", vf[i], f, atan[i])
}
} }
for _, i in vfatan_sc { for _, i in vfatan_sc {
if f := math.atan(vfatan_sc[i]); !alike(t, atan_sc[i], f) { f := math.atan(vfatan_sc[i])
tc.errorf(t, "math.atan(%.15g) = %.15g, want %.15g", vfatan_sc[i], f, atan_sc[i]) testing.expectf(t, alike(t, atan_sc[i], f), "math.atan(%.15g) = %.15g, want %.15g", vfatan_sc[i], f, atan_sc[i])
}
} }
} }
@@ -1147,84 +1098,72 @@ test_atan :: proc(t: ^testing.T) {
test_atanh :: proc(t: ^testing.T) { test_atanh :: proc(t: ^testing.T) {
for _, i in vf { for _, i in vf {
a := vf[i] / 10 a := vf[i] / 10
if f := math.atanh(a); !veryclose(t, atanh[i], f) { f := math.atanh(a)
tc.errorf(t, "math.atanh(%.15g) = %.15g, want %.15g", a, f, atanh[i]) testing.expectf(t, veryclose(t, atanh[i], f), "math.atanh(%.15g) = %.15g, want %.15g", a, f, atanh[i])
}
} }
for _, i in vfatanh_sc { for _, i in vfatanh_sc {
if f := math.atanh(vfatanh_sc[i]); !alike(t, atanh_sc[i], f) { f := math.atanh(vfatanh_sc[i])
tc.errorf(t, "math.atanh(%.15g) = %.15g, want %.15g", vfatanh_sc[i], f, atanh_sc[i]) testing.expectf(t, alike(t, atanh_sc[i], f), "math.atanh(%.15g) = %.15g, want %.15g", vfatanh_sc[i], f, atanh_sc[i])
}
} }
} }
@test @test
test_atan2 :: proc(t: ^testing.T) { test_atan2 :: proc(t: ^testing.T) {
for _, i in vf { for _, i in vf {
if f := math.atan2(10, vf[i]); !veryclose(t, atan2[i], f) { f := math.atan2(10, vf[i])
tc.errorf(t, "math.atan2(10, %.15g) = %.15g, want %.15g", vf[i], f, atan2[i]) testing.expectf(t, veryclose(t, atan2[i], f), "math.atan2(10, %.15g) = %.15g, want %.15g", vf[i], f, atan2[i])
}
} }
for _, i in vfatan2_sc { for _, i in vfatan2_sc {
if f := math.atan2(vfatan2_sc[i][0], vfatan2_sc[i][1]); !alike(t, atan2_sc[i], f) { f := math.atan2(vfatan2_sc[i][0], vfatan2_sc[i][1])
tc.errorf(t, "math.atan2(%.15g, %.15g) = %.15g, want %.15g", vfatan2_sc[i][0], vfatan2_sc[i][1], f, atan2_sc[i]) testing.expectf(t, alike(t, atan2_sc[i], f), "math.atan2(%.15g, %.15g) = %.15g, want %.15g", vfatan2_sc[i][0], vfatan2_sc[i][1], f, atan2_sc[i])
}
} }
} }
@test @test
test_cos :: proc(t: ^testing.T) { test_cos :: proc(t: ^testing.T) {
for _, i in vf { for _, i in vf {
if f := math.cos(vf[i]); !veryclose(t, cos[i], f) { f := math.cos(vf[i])
tc.errorf(t, "math.cos(%.15g) = %.15g, want %.15g", vf[i], f, cos[i]) testing.expectf(t, veryclose(t, cos[i], f), "math.cos(%.15g) = %.15g, want %.15g", vf[i], f, cos[i])
}
} }
for _, i in vfcos_sc { for _, i in vfcos_sc {
if f := math.cos(vfcos_sc[i]); !alike(t, cos_sc[i], f) { f := math.cos(vfcos_sc[i])
tc.errorf(t, "math.cos(%.15g) = %.15g, want %.15g", vfcos_sc[i], f, cos_sc[i]) testing.expectf(t, alike(t, cos_sc[i], f), "math.cos(%.15g) = %.15g, want %.15g", vfcos_sc[i], f, cos_sc[i])
}
} }
} }
@test @test
test_cosh :: proc(t: ^testing.T) { test_cosh :: proc(t: ^testing.T) {
for _, i in vf { for _, i in vf {
if f := math.cosh(vf[i]); !close(t, cosh[i], f) { f := math.cosh(vf[i])
tc.errorf(t, "math.cosh(%.15g) = %.15g, want %.15g", vf[i], f, cosh[i]) testing.expectf(t, close(t, cosh[i], f), "math.cosh(%.15g) = %.15g, want %.15g", vf[i], f, cosh[i])
}
} }
for _, i in vfcosh_sc { for _, i in vfcosh_sc {
if f := math.cosh(vfcosh_sc[i]); !alike(t, cosh_sc[i], f) { f := math.cosh(vfcosh_sc[i])
tc.errorf(t, "math.cosh(%.15g) = %.15g, want %.15g", vfcosh_sc[i], f, cosh_sc[i]) testing.expectf(t, alike(t, cosh_sc[i], f), "math.cosh(%.15g) = %.15g, want %.15g", vfcosh_sc[i], f, cosh_sc[i])
}
} }
} }
@test @test
test_sin :: proc(t: ^testing.T) { test_sin :: proc(t: ^testing.T) {
for _, i in vf { for _, i in vf {
if f := math.sin(vf[i]); !veryclose(t, sin[i], f) { f := math.sin(vf[i])
tc.errorf(t, "math.sin(%.15g) = %.15g, want %.15g", vf[i], f, sin[i]) testing.expectf(t, veryclose(t, sin[i], f), "math.sin(%.15g) = %.15g, want %.15g", vf[i], f, sin[i])
}
} }
for _, i in vfsin_sc { for _, i in vfsin_sc {
if f := math.sin(vfsin_sc[i]); !alike(t, sin_sc[i], f) { f := math.sin(vfsin_sc[i])
tc.errorf(t, "math.sin(%.15g) = %.15g, want %.15g", vfsin_sc[i], f, sin_sc[i]) testing.expectf(t, alike(t, sin_sc[i], f), "math.sin(%.15g) = %.15g, want %.15g", vfsin_sc[i], f, sin_sc[i])
}
} }
} }
@test @test
test_sinh :: proc(t: ^testing.T) { test_sinh :: proc(t: ^testing.T) {
for _, i in vf { for _, i in vf {
if f := math.sinh(vf[i]); !close(t, sinh[i], f) { f := math.sinh(vf[i])
tc.errorf(t, "math.sinh(%.15g) = %.15g, want %.15g", vf[i], f, sinh[i]) testing.expectf(t, close(t, sinh[i], f), "math.sinh(%.15g) = %.15g, want %.15g", vf[i], f, sinh[i])
}
} }
for _, i in vfsinh_sc { for _, i in vfsinh_sc {
if f := math.sinh(vfsinh_sc[i]); !alike(t, sinh_sc[i], f) { f := math.sinh(vfsinh_sc[i])
tc.errorf(t, "math.sinh(%.15g) = %.15g, want %.15g", vfsinh_sc[i], f, sinh_sc[i]) testing.expectf(t, alike(t, sinh_sc[i], f), "math.sinh(%.15g) = %.15g, want %.15g", vfsinh_sc[i], f, sinh_sc[i])
}
} }
} }
@@ -1232,38 +1171,33 @@ test_sinh :: proc(t: ^testing.T) {
test_sqrt :: proc(t: ^testing.T) { test_sqrt :: proc(t: ^testing.T) {
for _, i in vf { for _, i in vf {
a := abs(vf[i]) a := abs(vf[i])
if f := math.sqrt(a); !veryclose(t, sqrt[i], f) { f := math.sqrt(a)
tc.errorf(t, "math.sqrt(%.15g) = %.15g, want %.15g", a, f, sqrt[i]) testing.expectf(t, veryclose(t, sqrt[i], f), "math.sqrt(%.15g) = %.15g, want %.15g", a, f, sqrt[i])
}
} }
} }
@test @test
test_tan :: proc(t: ^testing.T) { test_tan :: proc(t: ^testing.T) {
for _, i in vf { for _, i in vf {
if f := math.tan(vf[i]); !veryclose(t, tan[i], f) { f := math.tan(vf[i])
tc.errorf(t, "math.tan(%.15g) = %.15g, want %.15g", vf[i], f, tan[i]) testing.expectf(t, veryclose(t, tan[i], f), "math.tan(%.15g) = %.15g, want %.15g", vf[i], f, tan[i])
}
} }
// same special cases as Sin // same special cases as Sin
for _, i in vfsin_sc { for _, i in vfsin_sc {
if f := math.tan(vfsin_sc[i]); !alike(t, sin_sc[i], f) { f := math.tan(vfsin_sc[i])
tc.errorf(t, "math.tan(%.15g) = %.15g, want %.15g", vfsin_sc[i], f, sin_sc[i]) testing.expectf(t, alike(t, sin_sc[i], f), "math.tan(%.15g) = %.15g, want %.15g", vfsin_sc[i], f, sin_sc[i])
}
} }
} }
@test @test
test_tanh :: proc(t: ^testing.T) { test_tanh :: proc(t: ^testing.T) {
for _, i in vf { for _, i in vf {
if f := math.tanh(vf[i]); !veryclose(t, tanh[i], f) { f := math.tanh(vf[i])
tc.errorf(t, "math.tanh(%.15g) = %.15g, want %.15g", vf[i], f, tanh[i]) testing.expectf(t, veryclose(t, tanh[i], f), "math.tanh(%.15g) = %.15g, want %.15g", vf[i], f, tanh[i])
}
} }
for _, i in vftanh_sc { for _, i in vftanh_sc {
if f := math.tanh(vftanh_sc[i]); !alike(t, tanh_sc[i], f) { f := math.tanh(vftanh_sc[i])
tc.errorf(t, "math.tanh(%.15g) = %.15g, want %.15g", vftanh_sc[i], f, tanh_sc[i]) testing.expectf(t, alike(t, tanh_sc[i], f), "math.tanh(%.15g) = %.15g, want %.15g", vftanh_sc[i], f, tanh_sc[i])
}
} }
} }
@@ -1273,9 +1207,7 @@ test_large_cos :: proc(t: ^testing.T) {
for _, i in vf { for _, i in vf {
f1 := cosLarge[i] f1 := cosLarge[i]
f2 := math.cos(vf[i] + large) f2 := math.cos(vf[i] + large)
if !close(t, f1, f2) { testing.expectf(t, close(t, f1, f2), "math.cos(%.15g) = %.15g, want %.15g", vf[i]+large, f2, f1)
tc.errorf(t, "math.cos(%.15g) = %.15g, want %.15g", vf[i]+large, f2, f1)
}
} }
} }
@@ -1285,9 +1217,7 @@ test_large_sin :: proc(t: ^testing.T) {
for _, i in vf { for _, i in vf {
f1 := sinLarge[i] f1 := sinLarge[i]
f2 := math.sin(vf[i] + large) f2 := math.sin(vf[i] + large)
if !close(t, f1, f2) { testing.expectf(t, close(t, f1, f2), "math.sin(%.15g) = %.15g, want %.15g", vf[i]+large, f2, f1)
tc.errorf(t, "math.sin(%.15g) = %.15g, want %.15g", vf[i]+large, f2, f1)
}
} }
} }
@@ -1297,8 +1227,6 @@ test_large_tan :: proc(t: ^testing.T) {
for _, i in vf { for _, i in vf {
f1 := tanLarge[i] f1 := tanLarge[i]
f2 := math.tan(vf[i] + large) f2 := math.tan(vf[i] + large)
if !close(t, f1, f2) { testing.expectf(t, close(t, f1, f2), "math.tan(%.15g) = %.15g, want %.15g", vf[i]+large, f2, f1)
tc.errorf(t, "math.tan(%.15g) = %.15g, want %.15g", vf[i]+large, f2, f1)
}
} }
} }
+83 -193
View File
@@ -11,71 +11,12 @@
package test_core_net package test_core_net
import "core:testing" import "core:testing"
import "core:mem"
import "core:fmt"
import "core:net" import "core:net"
import "core:strconv" import "core:strconv"
import "core:sync" import "core:sync"
import "core:time" import "core:time"
import "core:thread" import "core:thread"
import "core:os" import "core:fmt"
_, _ :: time, thread
TEST_count := 0
TEST_fail := 0
t := &testing.T{}
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
_tracking_allocator := mem.Tracking_Allocator{}
print_tracking_allocator_report :: proc() {
for _, leak in _tracking_allocator.allocation_map {
fmt.printf("%v leaked %v bytes\n", leak.location, leak.size)
}
for bf in _tracking_allocator.bad_free_array {
fmt.printf("%v allocation %p was freed badly\n", bf.location, bf.memory)
}
}
main :: proc() {
mem.tracking_allocator_init(&_tracking_allocator, context.allocator)
context.allocator = mem.tracking_allocator(&_tracking_allocator)
address_parsing_test(t)
tcp_tests(t)
split_url_test(t)
join_url_test(t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
print_tracking_allocator_report()
if TEST_fail > 0 {
os.exit(1)
}
}
@test @test
address_parsing_test :: proc(t: ^testing.T) { address_parsing_test :: proc(t: ^testing.T) {
@@ -89,127 +30,66 @@ address_parsing_test :: proc(t: ^testing.T) {
} }
valid := len(vector.binstr) > 0 valid := len(vector.binstr) > 0
fmt.printf("%v %v\n", kind, vector.input)
msg := "-set a proper message-"
switch vector.family { switch vector.family {
case .IP4, .IP4_Alt: case .IP4, .IP4_Alt:
/* // Does `net.parse_ip4_address` think we parsed the address properly?
Does `net.parse_ip4_address` think we parsed the address properly?
*/
non_decimal := vector.family == .IP4_Alt non_decimal := vector.family == .IP4_Alt
any_addr := net.parse_address(vector.input, non_decimal)
parsed_ok := any_addr != nil
parsed: net.IP4_Address
any_addr := net.parse_address(vector.input, non_decimal) // Ensure that `parse_address` doesn't parse IPv4 addresses into IPv6 addreses by mistake.
parsed_ok := any_addr != nil
parsed: net.IP4_Address
/*
Ensure that `parse_address` doesn't parse IPv4 addresses into IPv6 addreses by mistake.
*/
switch addr in any_addr { switch addr in any_addr {
case net.IP4_Address: case net.IP4_Address:
parsed = addr parsed = addr
case net.IP6_Address: case net.IP6_Address:
parsed_ok = false parsed_ok = false
msg = fmt.tprintf("parse_address mistook %v as IPv6 address %04x", vector.input, addr) testing.expectf(t, false, "parse_address mistook %v as IPv6 address %04x", vector.input, addr)
expect(t, false, msg)
} }
if !parsed_ok && valid { if !parsed_ok && valid {
msg = fmt.tprintf("parse_ip4_address failed to parse %v, expected %v", vector.input, binstr_to_address(vector.binstr)) testing.expectf(t, parsed_ok == valid, "parse_ip4_address failed to parse %v, expected %v", vector.input, binstr_to_address(t, vector.binstr))
} else if parsed_ok && !valid { } else if parsed_ok && !valid {
msg = fmt.tprintf("parse_ip4_address parsed %v into %v, expected failure", vector.input, parsed) testing.expectf(t, parsed_ok == valid, "parse_ip4_address parsed %v into %v, expected failure", vector.input, parsed)
} }
expect(t, parsed_ok == valid, msg)
if valid && parsed_ok { if valid && parsed_ok {
actual_binary := address_to_binstr(parsed) actual_binary := address_to_binstr(parsed)
msg = fmt.tprintf("parse_ip4_address parsed %v into %v, expected %v", vector.input, actual_binary, vector.binstr) testing.expectf(t, actual_binary == vector.binstr, "parse_ip4_address parsed %v into %v, expected %v", vector.input, actual_binary, vector.binstr)
expect(t, actual_binary == vector.binstr, msg)
/* // Do we turn an address back into the same string properly? No point in testing the roundtrip if the first part failed.
Do we turn an address back into the same string properly?
No point in testing the roundtrip if the first part failed.
*/
if len(vector.output) > 0 && actual_binary == vector.binstr { if len(vector.output) > 0 && actual_binary == vector.binstr {
stringified := net.address_to_string(parsed) stringified := net.address_to_string(parsed)
msg = fmt.tprintf("address_to_string turned %v into %v, expected %v", parsed, stringified, vector.output) testing.expectf(t, stringified == vector.output, "address_to_string turned %v into %v, expected %v", parsed, stringified, vector.output)
expect(t, stringified == vector.output, msg)
} }
} }
case .IP6: case .IP6:
/* // Do we parse the address properly?
Do we parse the address properly?
*/
parsed, parsed_ok := net.parse_ip6_address(vector.input) parsed, parsed_ok := net.parse_ip6_address(vector.input)
if !parsed_ok && valid { if !parsed_ok && valid {
msg = fmt.tprintf("parse_ip6_address failed to parse %v, expected %04x", vector.input, binstr_to_address(vector.binstr)) testing.expectf(t, parsed_ok == valid, "parse_ip6_address failed to parse %v, expected %04x", vector.input, binstr_to_address(t, vector.binstr))
} else if parsed_ok && !valid { } else if parsed_ok && !valid {
msg = fmt.tprintf("parse_ip6_address parsed %v into %04x, expected failure", vector.input, parsed) testing.expectf(t, parsed_ok == valid, "parse_ip6_address parsed %v into %04x, expected failure", vector.input, parsed)
} }
expect(t, parsed_ok == valid, msg)
if valid && parsed_ok { if valid && parsed_ok {
actual_binary := address_to_binstr(parsed) actual_binary := address_to_binstr(parsed)
msg = fmt.tprintf("parse_ip6_address parsed %v into %v, expected %v", vector.input, actual_binary, vector.binstr) testing.expectf(t, actual_binary == vector.binstr, "parse_ip6_address parsed %v into %v, expected %v", vector.input, actual_binary, vector.binstr)
expect(t, actual_binary == vector.binstr, msg)
/* // Do we turn an address back into the same string properly? No point in testing the roundtrip if the first part failed.
Do we turn an address back into the same string properly?
No point in testing the roundtrip if the first part failed.
*/
if len(vector.output) > 0 && actual_binary == vector.binstr { if len(vector.output) > 0 && actual_binary == vector.binstr {
stringified := net.address_to_string(parsed) stringified := net.address_to_string(parsed)
msg = fmt.tprintf("address_to_string turned %v into %v, expected %v", parsed, stringified, vector.output) testing.expectf(t, stringified == vector.output, "address_to_string turned %v into %v, expected %v", parsed, stringified, vector.output)
expect(t, stringified == vector.output, msg)
} }
} }
} }
} }
} }
address_to_binstr :: proc(address: net.Address) -> (binstr: string) {
switch t in address {
case net.IP4_Address:
b := transmute(u32be)t
return fmt.tprintf("%08x", b)
case net.IP6_Address:
b := transmute(u128be)t
return fmt.tprintf("%32x", b)
case:
return ""
}
unreachable()
}
binstr_to_address :: proc(binstr: string) -> (address: net.Address) {
switch len(binstr) {
case 8: // IPv4
a, ok := strconv.parse_u64_of_base(binstr, 16)
expect(t, ok, "failed to parse test case bin string")
ipv4 := u32be(a)
return net.IP4_Address(transmute([4]u8)ipv4)
case 32: // IPv6
a, ok := strconv.parse_u128_of_base(binstr, 16)
expect(t, ok, "failed to parse test case bin string")
ipv4 := u128be(a)
return net.IP6_Address(transmute([8]u16be)ipv4)
case 0:
return nil
}
panic("Invalid test case")
}
Kind :: enum { Kind :: enum {
IP4, // Decimal IPv4 IP4, // Decimal IPv4
IP4_Alt, // Non-decimal address IP4_Alt, // Non-decimal address
@@ -223,10 +103,7 @@ IP_Address_Parsing_Test_Vector :: struct {
// Input address to try and parse. // Input address to try and parse.
input: string, input: string,
/* // Hexadecimal representation of the expected numeric value of the address. Zero length means input is invalid and the parser should report failure.
Hexadecimal representation of the expected numeric value of the address.
Zero length means input is invalid and the parser should report failure.
*/
binstr: string, binstr: string,
// Expected `address_to_string` output, if a valid input and this string is non-empty. // Expected `address_to_string` output, if a valid input and this string is non-empty.
@@ -335,38 +212,30 @@ IP_Address_Parsing_Test_Vectors :: []IP_Address_Parsing_Test_Vector{
{ .IP6, "c0a8", "", ""}, { .IP6, "c0a8", "", ""},
} }
tcp_tests :: proc(t: ^testing.T) { ENDPOINT_TWO_SERVERS := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 9991}
fmt.println("Testing two servers trying to bind to the same endpoint...") ENDPOINT_CLOSED_PORT := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 9992}
two_servers_binding_same_endpoint(t) ENDPOINT_SERVER_SENDS := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 9993}
fmt.println("Testing client connecting to a closed port...")
client_connects_to_closed_port(t)
fmt.println("Testing client sending server data...")
client_sends_server_data(t)
}
ENDPOINT := net.Endpoint{
net.IP4_Address{127, 0, 0, 1},
9999,
}
@(test) @(test)
two_servers_binding_same_endpoint :: proc(t: ^testing.T) { two_servers_binding_same_endpoint :: proc(t: ^testing.T) {
skt1, err1 := net.listen_tcp(ENDPOINT) skt1, err1 := net.listen_tcp(ENDPOINT_TWO_SERVERS)
defer net.close(skt1) defer net.close(skt1)
skt2, err2 := net.listen_tcp(ENDPOINT) skt2, err2 := net.listen_tcp(ENDPOINT_TWO_SERVERS)
defer net.close(skt2) defer net.close(skt2)
expect(t, err1 == nil, "expected first server binding to endpoint to do so without error") testing.expect(t, err1 == nil, "expected first server binding to endpoint to do so without error")
expect(t, err2 == net.Bind_Error.Address_In_Use, "expected second server to bind to an endpoint to return .Address_In_Use") testing.expect(t, err2 == net.Bind_Error.Address_In_Use, "expected second server to bind to an endpoint to return .Address_In_Use")
} }
@(test) @(test)
client_connects_to_closed_port :: proc(t: ^testing.T) { client_connects_to_closed_port :: proc(t: ^testing.T) {
skt, err := net.dial_tcp(ENDPOINT)
skt, err := net.dial_tcp(ENDPOINT_CLOSED_PORT)
defer net.close(skt) defer net.close(skt)
expect(t, err == net.Dial_Error.Refused, "expected dial of a closed endpoint to return .Refused") testing.expect(t, err == net.Dial_Error.Refused, "expected dial of a closed endpoint to return .Refused")
} }
@(test) @(test)
client_sends_server_data :: proc(t: ^testing.T) { client_sends_server_data :: proc(t: ^testing.T) {
CONTENT: string: "Hellope!" CONTENT: string: "Hellope!"
@@ -390,8 +259,8 @@ client_sends_server_data :: proc(t: ^testing.T) {
defer sync.wait_group_done(r.wg) defer sync.wait_group_done(r.wg)
if r.skt, r.err = net.dial_tcp(ENDPOINT); r.err != nil { if r.skt, r.err = net.dial_tcp(ENDPOINT_SERVER_SENDS); r.err != nil {
log(r.t, r.err) testing.expectf(r.t, false, "[tcp_client:dial_tcp] %v", r.err)
return return
} }
@@ -405,19 +274,17 @@ client_sends_server_data :: proc(t: ^testing.T) {
defer sync.wait_group_done(r.wg) defer sync.wait_group_done(r.wg)
log(r.t, "tcp_server listen") if r.skt, r.err = net.listen_tcp(ENDPOINT_SERVER_SENDS); r.err != nil {
if r.skt, r.err = net.listen_tcp(ENDPOINT); r.err != nil {
sync.wait_group_done(r.wg) sync.wait_group_done(r.wg)
log(r.t, r.err) testing.expectf(r.t, false, "[tcp_server:listen_tcp] %v", r.err)
return return
} }
sync.wait_group_done(r.wg) sync.wait_group_done(r.wg)
log(r.t, "tcp_server accept")
client: net.TCP_Socket client: net.TCP_Socket
if client, _, r.err = net.accept_tcp(r.skt.(net.TCP_Socket)); r.err != nil { if client, _, r.err = net.accept_tcp(r.skt.(net.TCP_Socket)); r.err != nil {
log(r.t, r.err) testing.expectf(r.t, false, "[tcp_server:accept_tcp] %v", r.err)
return return
} }
defer net.close(client) defer net.close(client)
@@ -437,10 +304,7 @@ client_sends_server_data :: proc(t: ^testing.T) {
thread_data[0].wg = &wg thread_data[0].wg = &wg
thread_data[0].tid = thread.create_and_start_with_data(&thread_data[0], tcp_server, context) thread_data[0].tid = thread.create_and_start_with_data(&thread_data[0], tcp_server, context)
log(t, "waiting for server to start listening")
sync.wait_group_wait(&wg) sync.wait_group_wait(&wg)
log(t, "starting up client")
sync.wait_group_add(&wg, 2) sync.wait_group_add(&wg, 2)
thread_data[1].t = t thread_data[1].t = t
@@ -454,20 +318,15 @@ client_sends_server_data :: proc(t: ^testing.T) {
net.close(thread_data[1].skt) net.close(thread_data[1].skt)
thread.destroy(thread_data[1].tid) thread.destroy(thread_data[1].tid)
} }
log(t, "waiting for threads to finish")
sync.wait_group_wait(&wg) sync.wait_group_wait(&wg)
log(t, "threads finished")
okay := thread_data[0].err == nil && thread_data[1].err == nil okay := thread_data[0].err == nil && thread_data[1].err == nil
msg := fmt.tprintf("Expected client and server to return `nil`, got %v and %v", thread_data[0].err, thread_data[1].err) testing.expectf(t, okay, "Expected client and server to return `nil`, got %v and %v", thread_data[0].err, thread_data[1].err)
expect(t, okay, msg)
received := string(thread_data[0].data[:thread_data[0].length]) received := string(thread_data[0].data[:thread_data[0].length])
okay = received == CONTENT okay = received == CONTENT
msg = fmt.tprintf("Expected client to send \"{}\", got \"{}\"", CONTENT, received) testing.expectf(t, okay, "Expected client to send \"{}\", got \"{}\"", CONTENT, received)
expect(t, okay, msg)
} }
URL_Test :: struct { URL_Test :: struct {
@@ -559,22 +418,15 @@ split_url_test :: proc(t: ^testing.T) {
delete(test.queries) delete(test.queries)
} }
msg := fmt.tprintf("Expected `net.split_url` to return %s, got %s", test.scheme, scheme) testing.expectf(t, scheme == test.scheme, "Expected `net.split_url` to return %s, got %s", test.scheme, scheme)
expect(t, scheme == test.scheme, msg) testing.expectf(t, host == test.host, "Expected `net.split_url` to return %s, got %s", test.host, host)
msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", test.host, host) testing.expectf(t, path == test.path, "Expected `net.split_url` to return %s, got %s", test.path, path)
expect(t, host == test.host, msg) testing.expectf(t, len(queries) == len(test.queries), "Expected `net.split_url` to return %d queries, got %d queries", len(test.queries), len(queries))
msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", test.path, path)
expect(t, path == test.path, msg)
msg = fmt.tprintf("Expected `net.split_url` to return %d queries, got %d queries", len(test.queries), len(queries))
expect(t, len(queries) == len(test.queries), msg)
for k, v in queries { for k, v in queries {
expected := test.queries[k] expected := test.queries[k]
msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", expected, v) testing.expectf(t, v == expected, "Expected `net.split_url` to return %s, got %s", expected, v)
expect(t, v == expected, msg)
} }
msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", test.fragment, fragment) testing.expectf(t, fragment == test.fragment, "Expected `net.split_url` to return %s, got %s", test.fragment, fragment)
expect(t, fragment == test.fragment, msg)
} }
} }
@@ -659,7 +511,45 @@ join_url_test :: proc(t: ^testing.T) {
for test_url in test.url { for test_url in test.url {
pass |= url == test_url pass |= url == test_url
} }
msg := fmt.tprintf("Expected `net.join_url` to return one of %s, got %s", test.url, url) testing.expectf(t, pass, "Expected `net.join_url` to return one of %s, got %s", test.url, url)
expect(t, pass, msg)
} }
} }
@(private)
address_to_binstr :: proc(address: net.Address) -> (binstr: string) {
switch t in address {
case net.IP4_Address:
b := transmute(u32be)t
return fmt.tprintf("%08x", b)
case net.IP6_Address:
b := transmute(u128be)t
return fmt.tprintf("%32x", b)
case:
return ""
}
unreachable()
}
@(private)
binstr_to_address :: proc(t: ^testing.T, binstr: string) -> (address: net.Address) {
switch len(binstr) {
case 8: // IPv4
a, ok := strconv.parse_u64_of_base(binstr, 16)
testing.expect(t, ok, "failed to parse test case bin string")
ipv4 := u32be(a)
return net.IP4_Address(transmute([4]u8)ipv4)
case 32: // IPv6
a, ok := strconv.parse_u128_of_base(binstr, 16)
testing.expect(t, ok, "failed to parse test case bin string")
ipv4 := u128be(a)
return net.IP6_Address(transmute([8]u16be)ipv4)
case 0:
return nil
}
panic("Invalid test case")
}
+12 -41
View File
@@ -1,58 +1,29 @@
package test_core_odin_parser package test_core_odin_parser
import "core:fmt"
import "core:odin/ast" import "core:odin/ast"
import "core:odin/parser" import "core:odin/parser"
import "core:os" import "base:runtime"
import "core:testing" import "core:testing"
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
main :: proc() {
t := testing.T{}
test_parse_demo(&t)
test_parse_bitfield(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
@test @test
test_parse_demo :: proc(t: ^testing.T) { test_parse_demo :: proc(t: ^testing.T) {
pkg, ok := parser.parse_package_from_path("examples/demo") context.allocator = context.temp_allocator
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
pkg, ok := parser.parse_package_from_path(ODIN_ROOT + "examples/demo")
expect(t, ok == true, "parser.parse_package_from_path failed") testing.expect(t, ok, "parser.parse_package_from_path failed")
for key, value in pkg.files { for key, value in pkg.files {
expect(t, value.syntax_error_count == 0, fmt.tprintf("%v should contain zero errors", key)) testing.expectf(t, value.syntax_error_count == 0, "%v should contain zero errors", key)
} }
} }
@test @test
test_parse_bitfield :: proc(t: ^testing.T) { test_parse_bitfield :: proc(t: ^testing.T) {
context.allocator = context.temp_allocator
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
file := ast.File{ file := ast.File{
fullpath = "test.odin", fullpath = "test.odin",
src = ` src = `
@@ -78,5 +49,5 @@ Foo :: bit_field uint {
p := parser.default_parser() p := parser.default_parser()
ok := parser.parse_file(&p, &file) ok := parser.parse_file(&p, &file)
expect(t, ok == true, "bad parse") testing.expect(t, ok, "bad parse")
} }
@@ -1,26 +1,19 @@
// Tests "path.odin" in "core:path/filepath". // Tests "path.odin" in "core:path/filepath".
// Must be run with `-collection:tests=` flag, e.g.
// ./odin run tests/core/path/filepath/test_core_filepath.odin -collection:tests=tests
package test_core_filepath package test_core_filepath
import "core:fmt" import "core:fmt"
import "core:path/filepath" import "core:path/filepath"
import "core:testing" import "core:testing"
import tc "tests:common"
main :: proc() {
t := testing.T{}
@(test)
test_split_list :: proc(t: ^testing.T) {
when ODIN_OS == .Windows { when ODIN_OS == .Windows {
test_split_list_windows(&t) test_split_list_windows(t)
} else { } else {
test_split_list_unix(&t) test_split_list_unix(t)
} }
tc.report(&t)
} }
@test
test_split_list_windows :: proc(t: ^testing.T) { test_split_list_windows :: proc(t: ^testing.T) {
Datum :: struct { Datum :: struct {
i: int, i: int,
@@ -41,12 +34,12 @@ test_split_list_windows :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i, fmt.tprintf("wrong data index: i %d != d.i %d\n", i, d.i)) assert(i == d.i, fmt.tprintf("wrong data index: i %d != d.i %d\n", i, d.i))
r := filepath.split_list(d.v) r := filepath.split_list(d.v)
defer delete(r) defer delete_split(r)
tc.expect(t, len(r) == len(d.e), fmt.tprintf("i:%d %s(%s) len(r) %d != len(d.e) %d", testing.expect(t, len(r) == len(d.e), fmt.tprintf("i:%d %s(%s) len(r) %d != len(d.e) %d",
i, #procedure, d.v, len(r), len(d.e))) i, #procedure, d.v, len(r), len(d.e)))
if len(r) == len(d.e) { if len(r) == len(d.e) {
for _, j in r { for _, j in r {
tc.expect(t, r[j] == d.e[j], fmt.tprintf("i:%d %s(%v) -> %v[%d] != %v", testing.expect(t, r[j] == d.e[j], fmt.tprintf("i:%d %s(%v) -> %v[%d] != %v",
i, #procedure, d.v, r[j], j, d.e[j])) i, #procedure, d.v, r[j], j, d.e[j]))
} }
} }
@@ -55,47 +48,43 @@ test_split_list_windows :: proc(t: ^testing.T) {
{ {
v := "" v := ""
r := filepath.split_list(v) r := filepath.split_list(v)
tc.expect(t, r == nil, fmt.tprintf("%s(%s) -> %v != nil", #procedure, v, r)) defer delete_split(r)
testing.expect(t, r == nil, fmt.tprintf("%s(%s) -> %v != nil", #procedure, v, r))
} }
{ {
v := "a" v := "a"
r := filepath.split_list(v) r := filepath.split_list(v)
defer delete(r) defer delete_split(r)
tc.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r))) testing.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r)))
if len(r) == 1 { if len(r) == 1 {
tc.expect(t, r[0] == "a", fmt.tprintf("%s(%v) -> %v[0] != a", #procedure, v, r[0])) testing.expect(t, r[0] == "a", fmt.tprintf("%s(%v) -> %v[0] != a", #procedure, v, r[0]))
} }
} }
} }
@test
test_split_list_unix :: proc(t: ^testing.T) { test_split_list_unix :: proc(t: ^testing.T) {
Datum :: struct { Datum :: struct {
i: int,
v: string, v: string,
e: [3]string, e: [3]string,
} }
@static data := []Datum{ @static data := []Datum{
{ 0, "/opt/butler:/home/fancykillerpanda/Projects/Odin/Odin:/usr/local/sbin", { "/opt/butler:/home/fancykillerpanda/Projects/Odin/Odin:/usr/local/sbin",
[3]string{"/opt/butler", "/home/fancykillerpanda/Projects/Odin/Odin", "/usr/local/sbin"} }, // Issue #1537 [3]string{"/opt/butler", "/home/fancykillerpanda/Projects/Odin/Odin", "/usr/local/sbin"} }, // Issue #1537
{ 1, "a::b", [3]string{"a", "", "b"} }, { "a::b", [3]string{"a", "", "b"} },
{ 2, "a:b:", [3]string{"a", "b", ""} }, { "a:b:", [3]string{"a", "b", ""} },
{ 3, ":a:b", [3]string{"", "a", "b"} }, { ":a:b", [3]string{"", "a", "b"} },
{ 4, "::", [3]string{"", "", ""} }, { "::", [3]string{"", "", ""} },
{ 5, "\"a:b\"c:d:\"f\"", [3]string{"a:bc", "d", "f"} }, { "\"a:b\"c:d:\"f\"", [3]string{"a:bc", "d", "f"} },
{ 6, "\"a:b:c\":d\":e\":f", [3]string{"a:b:c", "d:e", "f"} }, { "\"a:b:c\":d\":e\":f", [3]string{"a:b:c", "d:e", "f"} },
} }
for d, i in data { for d in data {
assert(i == d.i, fmt.tprintf("wrong data index: i %d != d.i %d\n", i, d.i))
r := filepath.split_list(d.v) r := filepath.split_list(d.v)
defer delete(r) defer delete_split(r)
tc.expect(t, len(r) == len(d.e), fmt.tprintf("i:%d %s(%s) len(r) %d != len(d.e) %d", testing.expectf(t, len(r) == len(d.e), "%s len(r) %d != len(d.e) %d", d.v, len(r), len(d.e))
i, #procedure, d.v, len(r), len(d.e)))
if len(r) == len(d.e) { if len(r) == len(d.e) {
for _, j in r { for _, j in r {
tc.expect(t, r[j] == d.e[j], fmt.tprintf("i:%d %s(%v) -> %v[%d] != %v", testing.expectf(t, r[j] == d.e[j], "%v -> %v[%d] != %v", d.v, r[j], j, d.e[j])
i, #procedure, d.v, r[j], j, d.e[j]))
} }
} }
} }
@@ -103,15 +92,23 @@ test_split_list_unix :: proc(t: ^testing.T) {
{ {
v := "" v := ""
r := filepath.split_list(v) r := filepath.split_list(v)
tc.expect(t, r == nil, fmt.tprintf("%s(%s) -> %v != nil", #procedure, v, r)) testing.expectf(t, r == nil, "'%s' -> '%v' != nil", v, r)
} }
{ {
v := "a" v := "a"
r := filepath.split_list(v) r := filepath.split_list(v)
defer delete(r) defer delete_split(r)
tc.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r))) testing.expectf(t, len(r) == 1, "'%s' len(r) %d != 1", v, len(r))
if len(r) == 1 { if len(r) == 1 {
tc.expect(t, r[0] == "a", fmt.tprintf("%s(%v) -> %v[0] != a", #procedure, v, r[0])) testing.expectf(t, r[0] == "a", "'%v' -> %v[0] != a", v, r[0])
} }
} }
} }
@(private)
delete_split :: proc(s: []string) {
for part in s {
delete(part)
}
delete(s)
}
+33 -54
View File
@@ -1,21 +1,8 @@
// Tests "core:reflect/reflect". // Tests "core:reflect/reflect".
// Must be run with `-collection:tests=` flag, e.g.
// ./odin run tests/core/reflect/test_core_reflect.odin -out=tests/core/test_core_reflect -collection:tests=./tests
package test_core_reflect package test_core_reflect
import "core:fmt"
import "core:reflect" import "core:reflect"
import "core:testing" import "core:testing"
import tc "tests:common"
main :: proc() {
t := testing.T{}
test_as_u64(&t)
test_as_f64(&t)
tc.report(&t)
}
@test @test
test_as_u64 :: proc(t: ^testing.T) { test_as_u64 :: proc(t: ^testing.T) {
@@ -31,9 +18,8 @@ test_as_u64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := reflect.as_u64(d.v) r, valid := reflect.as_u64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i8 %v) !valid\n", i, #procedure, d.v)) testing.expectf(t, valid, "i8 %v !valid", d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i8 %v) -> %v (0x%X) != %v (0x%X)\n", testing.expectf(t, r == d.e, "i8 %v -> %v (0x%X) != %v (0x%X)", d.v, r, r, d.e, d.e)
i, #procedure, d.v, r, r, d.e, d.e))
} }
} }
{ {
@@ -48,9 +34,8 @@ test_as_u64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := reflect.as_u64(d.v) r, valid := reflect.as_u64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i16 %v) !valid\n", i, #procedure, d.v)) testing.expectf(t, valid, "i16 %v !valid", d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i16 %v) -> %v (0x%X) != %v (0x%X)\n", testing.expectf(t, r == d.e, "i16 %v -> %v (0x%X) != %v (0x%X)", d.v, r, r, d.e, d.e)
i, #procedure, d.v, r, r, d.e, d.e))
} }
} }
{ {
@@ -65,9 +50,8 @@ test_as_u64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := reflect.as_u64(d.v) r, valid := reflect.as_u64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i32 %v) !valid\n", i, #procedure, d.v)) testing.expectf(t, valid, "i32 %v !valid", d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i32 %v) -> %v (0x%X) != %v (0x%X)\n", testing.expectf(t, r == d.e, "i32 %v -> %v (0x%X) != %v (0x%X)", d.v, r, r, d.e, d.e)
i, #procedure, d.v, r, r, d.e, d.e))
} }
} }
{ {
@@ -82,9 +66,8 @@ test_as_u64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := reflect.as_u64(d.v) r, valid := reflect.as_u64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i64 %v) !valid\n", i, #procedure, d.v)) testing.expectf(t, valid, "i64 %v !valid", d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i64 %v) -> %v (0x%X) != %v (0x%X)\n", testing.expectf(t, r == d.e, "i64 %v -> %v (0x%X) != %v (0x%X)", d.v, r, r, d.e, d.e)
i, #procedure, d.v, r, r, d.e, d.e))
} }
} }
{ {
@@ -102,9 +85,8 @@ test_as_u64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := reflect.as_u64(d.v) r, valid := reflect.as_u64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i128 %v) !valid\n", i, #procedure, d.v)) testing.expectf(t, valid, "i128 %v !valid", d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i128 %v) -> %v (0x%X) != %v (0x%X)\n", testing.expectf(t, r == d.e, "i128 %v -> %v (0x%X) != %v (0x%X)", d.v, r, r, d.e, d.e)
i, #procedure, d.v, r, r, d.e, d.e))
} }
} }
{ {
@@ -118,8 +100,8 @@ test_as_u64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := reflect.as_u64(d.v) r, valid := reflect.as_u64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(f16 %v) !valid\n", i, #procedure, d.v)) testing.expectf(t, valid, "f16 %v !valid", d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f16 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) testing.expectf(t, r == d.e, "f16 %v -> %v != %v", d.v, r, d.e)
} }
} }
{ {
@@ -132,8 +114,8 @@ test_as_u64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := reflect.as_u64(d.v) r, valid := reflect.as_u64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(f32 %v) !valid\n", i, #procedure, d.v)) testing.expectf(t, valid, "f32 %v !valid", d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f32 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) testing.expectf(t, r == d.e, "f32 %v -> %v != %v", d.v, r, d.e)
} }
} }
{ {
@@ -146,8 +128,8 @@ test_as_u64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := reflect.as_u64(d.v) r, valid := reflect.as_u64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(f64 %v) !valid\n", i, #procedure, d.v)) testing.expectf(t, valid, "f64 %v !valid", d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) testing.expectf(t, r == d.e, "f64 %v -> %v != %v", d.v, r, d.e)
} }
} }
} }
@@ -166,8 +148,8 @@ test_as_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := reflect.as_f64(d.v) r, valid := reflect.as_f64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i8 %v) !valid\n", i, #procedure, d.v)) testing.expectf(t, valid, "i8 %v !valid", d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i8 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) testing.expectf(t, r == d.e, "i8 %v -> %v != %v", d.v, r, d.e)
} }
} }
{ {
@@ -182,8 +164,8 @@ test_as_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := reflect.as_f64(d.v) r, valid := reflect.as_f64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i16 %v) !valid\n", i, #procedure, d.v)) testing.expectf(t, valid, "i16 %v !valid", d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i16 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) testing.expectf(t, r == d.e, "i16 %v -> %v != %v", d.v, r, d.e)
} }
} }
{ {
@@ -198,8 +180,8 @@ test_as_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := reflect.as_f64(d.v) r, valid := reflect.as_f64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i32 %v) !valid\n", i, #procedure, d.v)) testing.expectf(t, valid, "i32 %v !valid", d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i32 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) testing.expectf(t, r == d.e, "i32 %v -> %v != %v", d.v, r, d.e)
} }
} }
{ {
@@ -214,8 +196,8 @@ test_as_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := reflect.as_f64(d.v) r, valid := reflect.as_f64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i64 %v) !valid\n", i, #procedure, d.v)) testing.expectf(t, valid, "i64 %v !valid", d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) testing.expectf(t, r == d.e, "i64 %v -> %v != %v", d.v, r, d.e)
} }
} }
{ {
@@ -231,9 +213,8 @@ test_as_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := reflect.as_f64(d.v) r, valid := reflect.as_f64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i128 %v) !valid\n", i, #procedure, d.v)) testing.expectf(t, valid, "i128 %v !valid", d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i128 %v) -> %v (%H) != %v (%H)\n", testing.expectf(t, r == d.e, "i128 %v -> %v (%H) != %v (%H)", d.v, r, r, d.e, d.e)
i, #procedure, d.v, r, r, d.e, d.e))
} }
} }
{ {
@@ -247,9 +228,8 @@ test_as_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := reflect.as_f64(d.v) r, valid := reflect.as_f64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(f16 %v) !valid\n", i, #procedure, d.v)) testing.expectf(t, valid, "f16 %v !valid", d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f16 %v (%H)) -> %v (%H) != %v (%H)\n", testing.expectf(t, r == d.e, "f16 %v (%H) -> %v (%H) != %v (%H)", d.v, d.v, r, r, d.e, d.e)
i, #procedure, d.v, d.v, r, r, d.e, d.e))
} }
} }
{ {
@@ -262,9 +242,8 @@ test_as_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := reflect.as_f64(d.v) r, valid := reflect.as_f64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(f32 %v) !valid\n", i, #procedure, d.v)) testing.expectf(t, valid, "f32 %v !valid", d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f32 %v (%H)) -> %v (%H) != %v (%H)\n", testing.expectf(t, r == d.e, "f32 %v (%H) -> %v (%H) != %v (%H)", d.v, d.v, r, r, d.e, d.e)
i, #procedure, d.v, d.v, r, r, d.e, d.e))
} }
} }
{ {
@@ -277,8 +256,8 @@ test_as_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := reflect.as_f64(d.v) r, valid := reflect.as_f64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(f64 %v) !valid\n", i, #procedure, d.v)) testing.expectf(t, valid, "f64 %v !valid", d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) testing.expectf(t, r == d.e, "f64 %v -> %v != %v", d.v, r, d.e)
} }
} }
} }
+5 -38
View File
@@ -1,43 +1,10 @@
package test_core_runtime package test_core_runtime
import "core:fmt"
import "base:intrinsics" import "base:intrinsics"
import "core:mem" import "core:mem"
import "core:os"
import "core:reflect"
import "base:runtime" import "base:runtime"
import "core:testing" import "core:testing"
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect_value :: testing.expect_value
} else {
expect_value :: proc(t: ^testing.T, value, expected: $T, loc := #caller_location) -> bool where intrinsics.type_is_comparable(T) {
TEST_count += 1
ok := value == expected || reflect.is_nil(value) && reflect.is_nil(expected)
if !ok {
TEST_fail += 1
fmt.printf("[%v] expected %v, got %v\n", loc, expected, value)
}
return ok
}
}
main :: proc() {
t := testing.T{}
test_temp_allocator_big_alloc_and_alignment(&t)
test_temp_allocator_alignment_boundary(&t)
test_temp_allocator_returns_correct_size(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
// Tests that having space for the allocation, but not for the allocation and alignment // Tests that having space for the allocation, but not for the allocation and alignment
// is handled correctly. // is handled correctly.
@(test) @(test)
@@ -47,7 +14,7 @@ test_temp_allocator_alignment_boundary :: proc(t: ^testing.T) {
_, _ = mem.alloc(int(runtime.DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE)-120) _, _ = mem.alloc(int(runtime.DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE)-120)
_, err := mem.alloc(112, 32) _, err := mem.alloc(112, 32)
expect_value(t, err, nil) testing.expect(t, err == nil)
} }
// Tests that big allocations with big alignments are handled correctly. // Tests that big allocations with big alignments are handled correctly.
@@ -58,7 +25,7 @@ test_temp_allocator_big_alloc_and_alignment :: proc(t: ^testing.T) {
mappy: map[[8]int]int mappy: map[[8]int]int
err := reserve(&mappy, 50000) err := reserve(&mappy, 50000)
expect_value(t, err, nil) testing.expect(t, err == nil)
} }
@(test) @(test)
@@ -67,6 +34,6 @@ test_temp_allocator_returns_correct_size :: proc(t: ^testing.T) {
context.allocator = runtime.arena_allocator(&arena) context.allocator = runtime.arena_allocator(&arena)
bytes, err := mem.alloc_bytes(10, 16) bytes, err := mem.alloc_bytes(10, 16)
expect_value(t, err, nil) testing.expect(t, err == nil)
expect_value(t, len(bytes), 10) testing.expect(t, len(bytes) == 10)
} }
+39 -97
View File
@@ -1,56 +1,16 @@
package test_core_slice package test_core_slice
import "core:slice" import "core:slice"
import "core:strings"
import "core:testing" import "core:testing"
import "core:fmt"
import "core:os"
import "core:math/rand" import "core:math/rand"
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
main :: proc() {
t := testing.T{}
test_sort_with_indices(&t)
test_binary_search(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
@test @test
test_sort_with_indices :: proc(t: ^testing.T) { test_sort_with_indices :: proc(t: ^testing.T) {
seed := rand.uint64()
fmt.printf("Random seed: %v\n", seed)
// Test sizes are all prime. // Test sizes are all prime.
test_sizes :: []int{7, 13, 347, 1031, 10111, 100003} test_sizes :: []int{7, 13, 347, 1031, 10111, 100003}
for test_size in test_sizes { for test_size in test_sizes {
fmt.printf("Sorting %v random u64 values along with index.\n", test_size) r := rand.create(t.seed)
r := rand.create(seed)
vals := make([]u64, test_size) vals := make([]u64, test_size)
r_idx := make([]int, test_size) // Reverse index r_idx := make([]int, test_size) // Reverse index
@@ -61,7 +21,7 @@ test_sort_with_indices :: proc(t: ^testing.T) {
// Set up test values // Set up test values
for _, i in vals { for _, i in vals {
vals[i] = rand.uint64(&r) vals[i] = rand.uint64(&r)
} }
// Sort // Sort
@@ -69,7 +29,7 @@ test_sort_with_indices :: proc(t: ^testing.T) {
defer delete(f_idx) defer delete(f_idx)
// Verify sorted test values // Verify sorted test values
rand.init(&r, seed) rand.init(&r, t.seed)
for v, i in f_idx { for v, i in f_idx {
r_idx[v] = i r_idx[v] = i
@@ -79,14 +39,14 @@ test_sort_with_indices :: proc(t: ^testing.T) {
for v, i in vals { for v, i in vals {
if i > 0 { if i > 0 {
val_pass := v >= last val_pass := v >= last
expect(t, val_pass, "Expected values to have been sorted.") testing.expect(t, val_pass, "Expected randomized test values to have been sorted")
if !val_pass { if !val_pass {
break break
} }
} }
idx_pass := vals[r_idx[i]] == rand.uint64(&r) idx_pass := vals[r_idx[i]] == rand.uint64(&r)
expect(t, idx_pass, "Expected index to have been sorted.") testing.expect(t, idx_pass, "Expected index to have been sorted")
if !idx_pass { if !idx_pass {
break break
} }
@@ -97,16 +57,11 @@ test_sort_with_indices :: proc(t: ^testing.T) {
@test @test
test_sort_by_indices :: proc(t: ^testing.T) { test_sort_by_indices :: proc(t: ^testing.T) {
seed := rand.uint64()
fmt.printf("Random seed: %v\n", seed)
// Test sizes are all prime. // Test sizes are all prime.
test_sizes :: []int{7, 13, 347, 1031, 10111, 100003} test_sizes :: []int{7, 13, 347, 1031, 10111, 100003}
for test_size in test_sizes { for test_size in test_sizes {
fmt.printf("Sorting %v random u64 values along with index.\n", test_size) r := rand.create(t.seed)
r := rand.create(seed)
vals := make([]u64, test_size) vals := make([]u64, test_size)
r_idx := make([]int, test_size) // Reverse index r_idx := make([]int, test_size) // Reverse index
@@ -117,7 +72,7 @@ test_sort_by_indices :: proc(t: ^testing.T) {
// Set up test values // Set up test values
for _, i in vals { for _, i in vals {
vals[i] = rand.uint64(&r) vals[i] = rand.uint64(&r)
} }
// Sort // Sort
@@ -125,7 +80,7 @@ test_sort_by_indices :: proc(t: ^testing.T) {
defer delete(f_idx) defer delete(f_idx)
// Verify sorted test values // Verify sorted test values
rand.init(&r, seed) rand.init(&r, t.seed)
{ {
indices := make([]int, test_size) indices := make([]int, test_size)
@@ -138,7 +93,7 @@ test_sort_by_indices :: proc(t: ^testing.T) {
defer delete(sorted_indices) defer delete(sorted_indices)
for v, i in sorted_indices { for v, i in sorted_indices {
idx_pass := v == f_idx[i] idx_pass := v == f_idx[i]
expect(t, idx_pass, "Expected the sorted index to be the same as the result from sort_with_indices") testing.expect(t, idx_pass, "Expected the sorted index to be the same as the result from sort_with_indices")
if !idx_pass { if !idx_pass {
break break
} }
@@ -154,7 +109,7 @@ test_sort_by_indices :: proc(t: ^testing.T) {
slice.sort_by_indices_overwrite(indices, f_idx) slice.sort_by_indices_overwrite(indices, f_idx)
for v, i in indices { for v, i in indices {
idx_pass := v == f_idx[i] idx_pass := v == f_idx[i]
expect(t, idx_pass, "Expected the sorted index to be the same as the result from sort_with_indices") testing.expect(t, idx_pass, "Expected the sorted index to be the same as the result from sort_with_indices")
if !idx_pass { if !idx_pass {
break break
} }
@@ -174,7 +129,7 @@ test_sort_by_indices :: proc(t: ^testing.T) {
slice.sort_by_indices(indices, swap, f_idx) slice.sort_by_indices(indices, swap, f_idx)
for v, i in swap { for v, i in swap {
idx_pass := v == f_idx[i] idx_pass := v == f_idx[i]
expect(t, idx_pass, "Expected the sorted index to be the same as the result from sort_with_indices") testing.expect(t, idx_pass, "Expected the sorted index to be the same as the result from sort_with_indices")
if !idx_pass { if !idx_pass {
break break
} }
@@ -185,61 +140,48 @@ test_sort_by_indices :: proc(t: ^testing.T) {
@test @test
test_binary_search :: proc(t: ^testing.T) { test_binary_search :: proc(t: ^testing.T) {
builder := strings.Builder{}
defer strings.builder_destroy(&builder)
test_search :: proc(t: ^testing.T, b: ^strings.Builder, s: []i32, v: i32) -> (int, bool) {
log(t, fmt.sbprintf(b, "Searching for %v in %v", v, s))
strings.builder_reset(b)
index, found := slice.binary_search(s, v)
log(t, fmt.sbprintf(b, "index: %v, found: %v", index, found))
strings.builder_reset(b )
return index, found
}
index: int index: int
found: bool found: bool
s := []i32{0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55} s := []i32{0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55}
index, found = test_search(t, &builder, s, 13) index, found = slice.binary_search(s, 13)
expect(t, index == 9, "Expected index to be 9.") testing.expect(t, index == 9, "Expected index to be 9")
expect(t, found == true, "Expected found to be true.") testing.expect(t, found == true, "Expected found to be true")
index, found = test_search(t, &builder, s, 4) index, found = slice.binary_search(s, 4)
expect(t, index == 7, "Expected index to be 7.") testing.expect(t, index == 7, "Expected index to be 7.")
expect(t, found == false, "Expected found to be false.") testing.expect(t, found == false, "Expected found to be false.")
index, found = test_search(t, &builder, s, 100) index, found = slice.binary_search(s, 100)
expect(t, index == 13, "Expected index to be 13.") testing.expect(t, index == 13, "Expected index to be 13.")
expect(t, found == false, "Expected found to be false.") testing.expect(t, found == false, "Expected found to be false.")
index, found = test_search(t, &builder, s, 1) index, found = slice.binary_search(s, 1)
expect(t, index >= 1 && index <= 4, "Expected index to be 1, 2, 3, or 4.") testing.expect(t, index >= 1 && index <= 4, "Expected index to be 1, 2, 3, or 4.")
expect(t, found == true, "Expected found to be true.") testing.expect(t, found == true, "Expected found to be true.")
index, found = test_search(t, &builder, s, -1) index, found = slice.binary_search(s, -1)
expect(t, index == 0, "Expected index to be 0.") testing.expect(t, index == 0, "Expected index to be 0.")
expect(t, found == false, "Expected found to be false.") testing.expect(t, found == false, "Expected found to be false.")
a := []i32{} a := []i32{}
index, found = test_search(t, &builder, a, 13) index, found = slice.binary_search(a, 13)
expect(t, index == 0, "Expected index to be 0.") testing.expect(t, index == 0, "Expected index to be 0.")
expect(t, found == false, "Expected found to be false.") testing.expect(t, found == false, "Expected found to be false.")
b := []i32{1} b := []i32{1}
index, found = test_search(t, &builder, b, 13) index, found = slice.binary_search(b, 13)
expect(t, index == 1, "Expected index to be 1.") testing.expect(t, index == 1, "Expected index to be 1.")
expect(t, found == false, "Expected found to be false.") testing.expect(t, found == false, "Expected found to be false.")
index, found = test_search(t, &builder, b, 1) index, found = slice.binary_search(b, 1)
expect(t, index == 0, "Expected index to be 0.") testing.expect(t, index == 0, "Expected index to be 0.")
expect(t, found == true, "Expected found to be true.") testing.expect(t, found == true, "Expected found to be true.")
index, found = test_search(t, &builder, b, 0) index, found = slice.binary_search(b, 0)
expect(t, index == 0, "Expected index to be 0.") testing.expect(t, index == 0, "Expected index to be 0.")
expect(t, found == false, "Expected found to be false.") testing.expect(t, found == false, "Expected found to be false.")
} }
+24 -62
View File
@@ -2,81 +2,42 @@ package test_core_strings
import "core:strings" import "core:strings"
import "core:testing" import "core:testing"
import "core:fmt"
import "core:os"
import "base:runtime" import "base:runtime"
import "core:mem"
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
main :: proc() {
t := testing.T{}
test_index_any_small_string_not_found(&t)
test_index_any_larger_string_not_found(&t)
test_index_any_small_string_found(&t)
test_index_any_larger_string_found(&t)
test_cut(&t)
test_case_conversion(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
@test @test
test_index_any_small_string_not_found :: proc(t: ^testing.T) { test_index_any_small_string_not_found :: proc(t: ^testing.T) {
index := strings.index_any(".", "/:\"") index := strings.index_any(".", "/:\"")
expect(t, index == -1, "index_any should be negative") testing.expect(t, index == -1, "index_any should be negative")
} }
@test @test
test_index_any_larger_string_not_found :: proc(t: ^testing.T) { test_index_any_larger_string_not_found :: proc(t: ^testing.T) {
index := strings.index_any("aaaaaaaa.aaaaaaaa", "/:\"") index := strings.index_any("aaaaaaaa.aaaaaaaa", "/:\"")
expect(t, index == -1, "index_any should be negative") testing.expect(t, index == -1, "index_any should be negative")
} }
@test @test
test_index_any_small_string_found :: proc(t: ^testing.T) { test_index_any_small_string_found :: proc(t: ^testing.T) {
index := strings.index_any(".", "/:.\"") index := strings.index_any(".", "/:.\"")
expect(t, index == 0, "index_any should be 0") testing.expect(t, index == 0, "index_any should be 0")
} }
@test @test
test_index_any_larger_string_found :: proc(t: ^testing.T) { test_index_any_larger_string_found :: proc(t: ^testing.T) {
index := strings.index_any("aaaaaaaa:aaaaaaaa", "/:\"") index := strings.index_any("aaaaaaaa:aaaaaaaa", "/:\"")
expect(t, index == 8, "index_any should be 8") testing.expect(t, index == 8, "index_any should be 8")
} }
@test @test
test_last_index_any_small_string_found :: proc(t: ^testing.T) { test_last_index_any_small_string_found :: proc(t: ^testing.T) {
index := strings.last_index_any(".", "/:.\"") index := strings.last_index_any(".", "/:.\"")
expect(t, index == 0, "last_index_any should be 0") testing.expect(t, index == 0, "last_index_any should be 0")
} }
@test @test
test_last_index_any_small_string_not_found :: proc(t: ^testing.T) { test_last_index_any_small_string_not_found :: proc(t: ^testing.T) {
index := strings.last_index_any(".", "/:\"") index := strings.last_index_any(".", "/:\"")
expect(t, index == -1, "last_index_any should be -1") testing.expect(t, index == -1, "last_index_any should be -1")
} }
Cut_Test :: struct { Cut_Test :: struct {
@@ -100,9 +61,12 @@ test_cut :: proc(t: ^testing.T) {
res := strings.cut(test.input, test.offset, test.length) res := strings.cut(test.input, test.offset, test.length)
defer delete(res) defer delete(res)
msg := fmt.tprintf("cut(\"%v\", %v, %v) expected to return \"%v\", got \"%v\"", testing.expectf(
test.input, test.offset, test.length, test.output, res) t,
expect(t, res == test.output, msg) res == test.output,
"cut(\"%v\", %v, %v) expected to return \"%v\", got \"%v\"",
test.input, test.offset, test.length, test.output, res,
)
} }
} }
@@ -118,7 +82,7 @@ Case_Kind :: enum {
Ada_Case, Ada_Case,
} }
Case_Proc :: proc(r: string, allocator: runtime.Allocator) -> (string, mem.Allocator_Error) Case_Proc :: proc(r: string, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error)
test_cases := [Case_Kind]struct{s: string, p: Case_Proc}{ test_cases := [Case_Kind]struct{s: string, p: Case_Proc}{
.Lower_Space_Case = {"hellope world", to_lower_space_case}, .Lower_Space_Case = {"hellope world", to_lower_space_case},
@@ -132,33 +96,31 @@ test_cases := [Case_Kind]struct{s: string, p: Case_Proc}{
.Ada_Case = {"Hellope_World", to_ada_case}, .Ada_Case = {"Hellope_World", to_ada_case},
} }
to_lower_space_case :: proc(r: string, allocator: runtime.Allocator) -> (string, mem.Allocator_Error) { to_lower_space_case :: proc(r: string, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
return strings.to_delimiter_case(r, ' ', false, allocator) return strings.to_delimiter_case(r, ' ', false, allocator)
} }
to_upper_space_case :: proc(r: string, allocator: runtime.Allocator) -> (string, mem.Allocator_Error) { to_upper_space_case :: proc(r: string, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
return strings.to_delimiter_case(r, ' ', true, allocator) return strings.to_delimiter_case(r, ' ', true, allocator)
} }
// NOTE: we have these wrappers as having #optional_allocator_error changes the type to not be equivalent // NOTE: we have these wrappers as having #optional_allocator_error changes the type to not be equivalent
to_snake_case :: proc(r: string, allocator: runtime.Allocator) -> (string, mem.Allocator_Error) { return strings.to_snake_case(r, allocator) } to_snake_case :: proc(r: string, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) { return strings.to_snake_case(r, allocator) }
to_upper_snake_case :: proc(r: string, allocator: runtime.Allocator) -> (string, mem.Allocator_Error) { return strings.to_upper_snake_case(r, allocator) } to_upper_snake_case :: proc(r: string, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) { return strings.to_upper_snake_case(r, allocator) }
to_kebab_case :: proc(r: string, allocator: runtime.Allocator) -> (string, mem.Allocator_Error) { return strings.to_kebab_case(r, allocator) } to_kebab_case :: proc(r: string, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) { return strings.to_kebab_case(r, allocator) }
to_upper_kebab_case :: proc(r: string, allocator: runtime.Allocator) -> (string, mem.Allocator_Error) { return strings.to_upper_kebab_case(r, allocator) } to_upper_kebab_case :: proc(r: string, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) { return strings.to_upper_kebab_case(r, allocator) }
to_camel_case :: proc(r: string, allocator: runtime.Allocator) -> (string, mem.Allocator_Error) { return strings.to_camel_case(r, allocator) } to_camel_case :: proc(r: string, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) { return strings.to_camel_case(r, allocator) }
to_pascal_case :: proc(r: string, allocator: runtime.Allocator) -> (string, mem.Allocator_Error) { return strings.to_pascal_case(r, allocator) } to_pascal_case :: proc(r: string, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) { return strings.to_pascal_case(r, allocator) }
to_ada_case :: proc(r: string, allocator: runtime.Allocator) -> (string, mem.Allocator_Error) { return strings.to_ada_case(r, allocator) } to_ada_case :: proc(r: string, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) { return strings.to_ada_case(r, allocator) }
@test @test
test_case_conversion :: proc(t: ^testing.T) { test_case_conversion :: proc(t: ^testing.T) {
for entry in test_cases { for entry in test_cases {
for test_case, case_kind in test_cases { for test_case, case_kind in test_cases {
result, err := entry.p(test_case.s, context.allocator) result, err := entry.p(test_case.s, context.allocator)
msg := fmt.tprintf("ERROR: We got the allocation error '{}'\n", err) testing.expectf(t, err == nil, "ERROR: We got the allocation error '{}'\n", err)
expect(t, err == nil, msg)
defer delete(result) defer delete(result)
msg = fmt.tprintf("ERROR: Input `{}` to converter {} does not match `{}`, got `{}`.\n", test_case.s, case_kind, entry.s, result) testing.expectf(t, result == entry.s, "ERROR: Input `{}` to converter {} does not match `{}`, got `{}`.\n", test_case.s, case_kind, entry.s, result)
expect(t, result == entry.s, msg)
} }
} }
} }
+67 -102
View File
@@ -1,31 +1,9 @@
package test_core_text_i18n package test_core_text_i18n
import "core:mem" import "base:runtime"
import "core:fmt"
import "core:os"
import "core:testing" import "core:testing"
import "core:text/i18n" import "core:text/i18n"
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
T :: i18n.get T :: i18n.get
Test :: struct { Test :: struct {
@@ -37,25 +15,28 @@ Test :: struct {
Test_Suite :: struct { Test_Suite :: struct {
file: string, file: string,
loader: proc(string, i18n.Parse_Options, proc(int) -> int, mem.Allocator) -> (^i18n.Translation, i18n.Error), loader: proc(string, i18n.Parse_Options, proc(int) -> int, runtime.Allocator) -> (^i18n.Translation, i18n.Error),
plural: proc(int) -> int, plural: proc(int) -> int,
err: i18n.Error, err: i18n.Error,
options: i18n.Parse_Options, options: i18n.Parse_Options,
tests: []Test, tests: []Test,
} }
// Custom pluralizer for plur.mo TEST_SUITE_PATH :: ODIN_ROOT + "tests/core/assets/I18N/"
plur_mo_pluralizer :: proc(n: int) -> (slot: int) {
switch {
case n == 1: return 0
case n != 0 && n % 1_000_000 == 0: return 1
case: return 2
}
}
TESTS := []Test_Suite{ @(test)
{ test_custom_pluralizer :: proc(t: ^testing.T) {
file = "assets/I18N/plur.mo", // Custom pluralizer for plur.mo
plur_mo_pluralizer :: proc(n: int) -> (slot: int) {
switch {
case n == 1: return 0
case n != 0 && n % 1_000_000 == 0: return 1
case: return 2
}
}
test(t, {
file = TEST_SUITE_PATH + "plur.mo",
loader = i18n.parse_mo_file, loader = i18n.parse_mo_file,
plural = plur_mo_pluralizer, plural = plur_mo_pluralizer,
tests = { tests = {
@@ -66,14 +47,16 @@ TESTS := []Test_Suite{
{"", "Message1/plural", "This is message 1", 1}, {"", "Message1/plural", "This is message 1", 1},
{"", "Message1/plural", "This is message 1 - plural A", 1_000_000}, {"", "Message1/plural", "This is message 1 - plural A", 1_000_000},
{"", "Message1/plural", "This is message 1 - plural B", 42}, {"", "Message1/plural", "This is message 1 - plural B", 42},
// This isn't in the catalog, so should ruturn the key. // This isn't in the catalog, so should ruturn the key.
{"", "Come visit us on Discord!", "Come visit us on Discord!", 1}, {"", "Come visit us on Discord!", "Come visit us on Discord!", 1},
}, },
}, })
}
{ @(test)
file = "assets/I18N/mixed_context.mo", test_mixed_context :: proc(t: ^testing.T) {
test(t, {
file = TEST_SUITE_PATH + "mixed_context.mo",
loader = i18n.parse_mo_file, loader = i18n.parse_mo_file,
plural = nil, plural = nil,
tests = { tests = {
@@ -84,19 +67,25 @@ TESTS := []Test_Suite{
// This isn't in the catalog, so should ruturn the key. // This isn't in the catalog, so should ruturn the key.
{"", "Come visit us on Discord!", "Come visit us on Discord!", 1}, {"", "Come visit us on Discord!", "Come visit us on Discord!", 1},
}, },
}, })
}
{ @(test)
file = "assets/I18N/mixed_context.mo", test_mixed_context_dupe :: proc(t: ^testing.T) {
test(t, {
file = TEST_SUITE_PATH + "mixed_context.mo",
loader = i18n.parse_mo_file, loader = i18n.parse_mo_file,
plural = nil, plural = nil,
// Message1 exists twice, once within Context, which has been merged into "" // Message1 exists twice, once within Context, which has been merged into ""
err = .Duplicate_Key, err = .Duplicate_Key,
options = {merge_sections = true}, options = {merge_sections = true},
}, })
}
{ @(test)
file = "assets/I18N/nl_NL.mo", test_nl_mo :: proc(t: ^testing.T) {
test(t, {
file = TEST_SUITE_PATH + "nl_NL.mo",
loader = i18n.parse_mo_file, loader = i18n.parse_mo_file,
plural = nil, // Default pluralizer plural = nil, // Default pluralizer
tests = { tests = {
@@ -111,12 +100,13 @@ TESTS := []Test_Suite{
// This isn't in the catalog, so should ruturn the key. // This isn't in the catalog, so should ruturn the key.
{"", "Come visit us on Discord!", "Come visit us on Discord!", 1}, {"", "Come visit us on Discord!", "Come visit us on Discord!", 1},
}, },
}, })
}
@(test)
// QT Linguist with default loader options. test_qt_linguist :: proc(t: ^testing.T) {
{ test(t, {
file = "assets/I18N/nl_NL-qt-ts.ts", file = TEST_SUITE_PATH + "nl_NL-qt-ts.ts",
loader = i18n.parse_qt_linguist_file, loader = i18n.parse_qt_linguist_file,
plural = nil, // Default pluralizer plural = nil, // Default pluralizer
tests = { tests = {
@@ -131,11 +121,13 @@ TESTS := []Test_Suite{
{"", "Come visit us on Discord!", "Come visit us on Discord!", 1}, {"", "Come visit us on Discord!", "Come visit us on Discord!", 1},
{"Fake_Section", "Come visit us on Discord!", "Come visit us on Discord!", 1}, {"Fake_Section", "Come visit us on Discord!", "Come visit us on Discord!", 1},
}, },
}, })
}
// QT Linguist, merging sections. @(test)
{ test_qt_linguist_merge_sections :: proc(t: ^testing.T) {
file = "assets/I18N/nl_NL-qt-ts.ts", test(t, {
file = TEST_SUITE_PATH + "nl_NL-qt-ts.ts",
loader = i18n.parse_qt_linguist_file, loader = i18n.parse_qt_linguist_file,
plural = nil, // Default pluralizer plural = nil, // Default pluralizer
options = {merge_sections = true}, options = {merge_sections = true},
@@ -154,65 +146,38 @@ TESTS := []Test_Suite{
{"apple_count", "%d apple(s)", "%d apple(s)", 1}, {"apple_count", "%d apple(s)", "%d apple(s)", 1},
{"apple_count", "%d apple(s)", "%d apple(s)", 42}, {"apple_count", "%d apple(s)", "%d apple(s)", 42},
}, },
}, })
}
// QT Linguist, merging sections. Expecting .Duplicate_Key error because same key exists in more than 1 section. @(test)
{ test_qt_linguist_duplicate_key_err :: proc(t: ^testing.T) {
file = "assets/I18N/duplicate-key.ts", test(t, { // QT Linguist, merging sections. Expecting .Duplicate_Key error because same key exists in more than 1 section.
file = TEST_SUITE_PATH + "duplicate-key.ts",
loader = i18n.parse_qt_linguist_file, loader = i18n.parse_qt_linguist_file,
plural = nil, // Default pluralizer plural = nil, // Default pluralizer
options = {merge_sections = true}, options = {merge_sections = true},
err = .Duplicate_Key, err = .Duplicate_Key,
}, })
}
// QT Linguist, not merging sections. Shouldn't return error despite same key existing in more than 1 section. @(test)
{ test_qt_linguist_duplicate_key :: proc(t: ^testing.T) {
file = "assets/I18N/duplicate-key.ts", test(t, { // QT Linguist, not merging sections. Shouldn't return error despite same key existing in more than 1 section.
file = TEST_SUITE_PATH + "duplicate-key.ts",
loader = i18n.parse_qt_linguist_file, loader = i18n.parse_qt_linguist_file,
plural = nil, // Default pluralizer plural = nil, // Default pluralizer
}, })
} }
@test test :: proc(t: ^testing.T, suite: Test_Suite, loc := #caller_location) {
tests :: proc(t: ^testing.T) { cat, err := suite.loader(suite.file, suite.options, suite.plural, context.allocator)
cat: ^i18n.Translation testing.expectf(t, err == suite.err, "Expected loading %v to return %v, got %v", suite.file, suite.err, err, loc=loc)
err: i18n.Error
for suite in TESTS { if err == .None {
cat, err = suite.loader(suite.file, suite.options, suite.plural, context.allocator) for test in suite.tests {
val := T(test.section, test.key, test.n, cat)
msg := fmt.tprintf("Expected loading %v to return %v, got %v", suite.file, suite.err, err) testing.expectf(t, val == test.val, "Expected key `%v` from section `%v`'s form for value `%v` to equal `%v`, got `%v`", test.key, test.section, test.n, test.val, val, loc=loc)
expect(t, err == suite.err, msg)
if err == .None {
for test in suite.tests {
val := T(test.section, test.key, test.n, cat)
msg = fmt.tprintf("Expected key `%v` from section `%v`'s form for value `%v` to equal `%v`, got `%v`", test.key, test.section, test.n, test.val, val)
expect(t, val == test.val, msg)
}
}
i18n.destroy(cat)
}
}
main :: proc() {
track: mem.Tracking_Allocator
mem.tracking_allocator_init(&track, context.allocator)
context.allocator = mem.tracking_allocator(&track)
t := testing.T{}
tests(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
if len(track.allocation_map) > 0 {
fmt.println()
for _, v in track.allocation_map {
fmt.printf("%v Leaked %v bytes.\n", v.location, v.size)
} }
} }
i18n.destroy(cat)
} }
+62 -97
View File
@@ -2,31 +2,6 @@ package test_strlib
import "core:text/match" import "core:text/match"
import "core:testing" import "core:testing"
import "core:fmt"
import "core:os"
import "core:io"
TEST_count: int
TEST_fail: int
// inline expect with custom props
failed :: proc(t: ^testing.T, ok: bool, loc := #caller_location) -> bool {
TEST_count += 1
if !ok {
fmt.wprintf(t.w, "%v: ", loc)
t.error_count += 1
TEST_fail += 1
}
return !ok
}
expect :: testing.expect
logf :: proc(t: ^testing.T, format: string, args: ..any) {
fmt.wprintf(t.w, format, ..args)
}
// find correct byte offsets // find correct byte offsets
@test @test
@@ -61,18 +36,17 @@ test_find :: proc(t: ^testing.T) {
{ "helelo", "h.-l", 0, { 0, 3, true } }, { "helelo", "h.-l", 0, { 0, 3, true } },
} }
for entry, i in ENTRIES { for entry in ENTRIES {
matcher := match.matcher_init(entry.s, entry.p, entry.offset) matcher := match.matcher_init(entry.s, entry.p, entry.offset)
start, end, ok := match.matcher_find(&matcher) start, end, ok := match.matcher_find(&matcher)
success := entry.match.ok == ok && start == entry.match.start && end == entry.match.end success := entry.match.ok == ok && start == entry.match.start && end == entry.match.end
if failed(t, success) { testing.expectf(
logf(t, "Find %d failed!\n", i) t,
logf(t, "\tHAYSTACK %s\tPATTERN %s\n", entry.s, entry.p) success,
logf(t, "\tSTART: %d == %d?\n", entry.match.start, start) "HAYSTACK %q PATTERN %q, START: %d == %d? END: %d == %d? Err: %v Length %d",
logf(t, "\tEND: %d == %d?\n", entry.match.end, end) entry.s, entry.p, entry.match.start, start, entry.match.end, end, matcher.err, matcher.captures_length,
logf(t, "\tErr: %v\tLength %d\n", matcher.err, matcher.captures_length) )
}
} }
} }
@@ -178,17 +152,17 @@ test_match :: proc(t: ^testing.T) {
{ "testing _this_ out", "%b_", "", false }, { "testing _this_ out", "%b_", "", false },
} }
for entry, i in ENTRIES { for entry in ENTRIES {
matcher := match.matcher_init(entry.s, entry.p) matcher := match.matcher_init(entry.s, entry.p)
result, ok := match.matcher_match(&matcher) result, ok := match.matcher_match(&matcher)
success := entry.ok == ok && result == entry.result success := entry.ok == ok && result == entry.result
if failed(t, success) { testing.expectf(
logf(t, "Match %d failed!\n", i) t,
logf(t, "\tHAYSTACK %s\tPATTERN %s\n", entry.s, entry.p) success,
logf(t, "\tResults: WANTED %s\tGOT %s\n", entry.result, result) "HAYSTACK %q PATTERN %q WANTED %q GOT %q Err: %v Length %d",
logf(t, "\tErr: %v\tLength %d\n", matcher.err, matcher.captures_length) entry.s, entry.p, entry.result, result, matcher.err, matcher.captures_length,
} )
} }
} }
@@ -203,19 +177,23 @@ test_captures :: proc(t: ^testing.T) {
compare_captures :: proc(t: ^testing.T, test: ^Temp, haystack: string, comp: []string, loc := #caller_location) { compare_captures :: proc(t: ^testing.T, test: ^Temp, haystack: string, comp: []string, loc := #caller_location) {
length, err := match.find_aux(haystack, test.pattern, 0, false, &test.captures) length, err := match.find_aux(haystack, test.pattern, 0, false, &test.captures)
result := len(comp) == length && err == .OK result := len(comp) == length && err == .OK
if failed(t, result == true) { testing.expectf(
logf(t, "Captures Compare Failed!\n") t,
logf(t, "\tErr: %v\n", err) result,
logf(t, "\tLengths: %v != %v\n", len(comp), length) "Captures Compare Failed! Lengths: %v != %v Err: %v",
} len(comp), length, err,
)
for i in 0..<length { for i in 0..<length {
cap := test.captures[i] cap := test.captures[i]
text := haystack[cap.byte_start:cap.byte_end] text := haystack[cap.byte_start:cap.byte_end]
if failed(t, comp[i] == text) { testing.expectf(
logf(t, "Capture don't equal -> %s != %s\n", comp[i], text) t,
} comp[i] == text,
"Capture don't equal -> %q != %q\n",
comp[i], text,
)
} }
} }
@@ -224,11 +202,12 @@ test_captures :: proc(t: ^testing.T) {
length, err := match.find_aux(haystack, test.pattern, 0, false, &test.captures) length, err := match.find_aux(haystack, test.pattern, 0, false, &test.captures)
result := length > 0 && err == .OK result := length > 0 && err == .OK
if failed(t, result == ok) { testing.expectf(
logf(t, "Capture match failed!\n") t,
logf(t, "\tErr: %v\n", err) result == ok,
logf(t, "\tLength: %v\n", length) "Capture match failed! Length: %v Pattern: %q Haystack: %q Err: %v",
} length, test.pattern, haystack, err,
)
} }
temp := Temp { pattern = "(one).+" } temp := Temp { pattern = "(one).+" }
@@ -253,15 +232,8 @@ test_captures :: proc(t: ^testing.T) {
cap2 := captures[2] cap2 := captures[2]
text1 := haystack[cap1.byte_start:cap1.byte_end] text1 := haystack[cap1.byte_start:cap1.byte_end]
text2 := haystack[cap2.byte_start:cap2.byte_end] text2 := haystack[cap2.byte_start:cap2.byte_end]
expect(t, text1 == "233", "Multi-Capture failed at 1") testing.expect(t, text1 == "233", "Multi-Capture failed at 1")
expect(t, text2 == "hello", "Multi-Capture failed at 2") testing.expect(t, text2 == "hello", "Multi-Capture failed at 2")
}
}
gmatch_check :: proc(t: ^testing.T, index: int, a: []string, b: string) {
if failed(t, a[index] == b) {
logf(t, "GMATCH %d failed!\n", index)
logf(t, "\t%s != %s\n", a[index], b)
} }
} }
@@ -298,9 +270,9 @@ test_gmatch :: proc(t: ^testing.T) {
@test @test
test_gsub :: proc(t: ^testing.T) { test_gsub :: proc(t: ^testing.T) {
result := match.gsub("testing123testing", "%d+", " sup ", context.temp_allocator) result := match.gsub("testing123testing", "%d+", " sup ", context.temp_allocator)
expect(t, result == "testing sup testing", "GSUB 0: failed") testing.expect(t, result == "testing sup testing", "GSUB 0: failed")
result = match.gsub("testing123testing", "%a+", "345", context.temp_allocator) result = match.gsub("testing123testing", "%a+", "345", context.temp_allocator)
expect(t, result == "345123345", "GSUB 1: failed") testing.expect(t, result == "345123345", "GSUB 1: failed")
} }
@test @test
@@ -313,10 +285,12 @@ test_gfind :: proc(t: ^testing.T) {
index: int index: int
for word in match.gfind(s, pattern, &captures) { for word in match.gfind(s, pattern, &captures) {
if failed(t, output[index] == word) { testing.expectf(
logf(t, "GFIND %d failed!\n", index) t,
logf(t, "\t%s != %s\n", output[index], word) output[index] == word,
} "GFIND %d failed! %q != %q",
index, output[index], word,
)
index += 1 index += 1
} }
} }
@@ -332,11 +306,12 @@ test_frontier :: proc(t: ^testing.T) {
call :: proc(data: rawptr, word: string, haystack: string, captures: []match.Match) { call :: proc(data: rawptr, word: string, haystack: string, captures: []match.Match) {
temp := cast(^Temp) data temp := cast(^Temp) data
if failed(temp.t, word == temp.output[temp.index]) { testing.expectf(
logf(temp.t, "GSUB_WITH %d failed!\n", temp.index) temp.t,
logf(temp.t, "\t%s != %s\n", temp.output[temp.index], word) word == temp.output[temp.index],
} "GSUB_WITH %d failed! %q != %q",
temp.index, temp.output[temp.index], word,
)
temp.index += 1 temp.index += 1
} }
@@ -369,31 +344,21 @@ test_case_insensitive :: proc(t: ^testing.T) {
pattern := match.pattern_case_insensitive("test", 256, context.temp_allocator) pattern := match.pattern_case_insensitive("test", 256, context.temp_allocator)
goal := "[tT][eE][sS][tT]" goal := "[tT][eE][sS][tT]"
if failed(t, pattern == goal) { testing.expectf(
logf(t, "Case Insensitive Pattern doesn't match result\n") t,
logf(t, "\t%s != %s\n", pattern, goal) pattern == goal,
} "Case Insensitive Pattern doesn't match result. %q != %q",
pattern, goal,
)
} }
} }
main :: proc() { @(private)
t: testing.T gmatch_check :: proc(t: ^testing.T, index: int, a: []string, b: string) {
stream := os.stream_from_handle(os.stdout) testing.expectf(
w := io.to_writer(stream) t,
t.w = w a[index] == b,
"GMATCH %d failed! %q != %q",
test_find(&t) index, a[index], b,
test_match(&t) )
test_captures(&t)
test_gmatch(&t)
test_gsub(&t)
test_gfind(&t)
test_frontier(&t)
test_utf8(&t)
test_case_insensitive(&t)
fmt.wprintf(w, "%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
} }
+12 -44
View File
@@ -2,39 +2,7 @@ package test_core_thread
import "core:testing" import "core:testing"
import "core:thread" import "core:thread"
import "core:fmt" import "base:intrinsics"
import "core:os"
TEST_count := 0
TEST_fail := 0
t := &testing.T{}
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
main :: proc() {
poly_data_test(t)
if TEST_fail > 0 {
os.exit(1)
}
}
@(test) @(test)
poly_data_test :: proc(_t: ^testing.T) { poly_data_test :: proc(_t: ^testing.T) {
@@ -46,7 +14,7 @@ poly_data_test :: proc(_t: ^testing.T) {
b: [MAX]byte = 8 b: [MAX]byte = 8
t1 := thread.create_and_start_with_poly_data(b, proc(b: [MAX]byte) { t1 := thread.create_and_start_with_poly_data(b, proc(b: [MAX]byte) {
b_expect: [MAX]byte = 8 b_expect: [MAX]byte = 8
expect(poly_data_test_t, b == b_expect, "thread poly data not correct") testing.expect(poly_data_test_t, b == b_expect, "thread poly data not correct")
}) })
defer free(t1) defer free(t1)
@@ -55,8 +23,8 @@ poly_data_test :: proc(_t: ^testing.T) {
t2 := thread.create_and_start_with_poly_data2(b1, b2, proc(b: [3]uintptr, b2: [MAX / 2]byte) { t2 := thread.create_and_start_with_poly_data2(b1, b2, proc(b: [3]uintptr, b2: [MAX / 2]byte) {
b_expect: [3]uintptr = 1 b_expect: [3]uintptr = 1
b2_expect: [MAX / 2]byte = 3 b2_expect: [MAX / 2]byte = 3
expect(poly_data_test_t, b == b_expect, "thread poly data not correct") testing.expect(poly_data_test_t, b == b_expect, "thread poly data not correct")
expect(poly_data_test_t, b2 == b2_expect, "thread poly data not correct") testing.expect(poly_data_test_t, b2 == b2_expect, "thread poly data not correct")
}) })
defer free(t2) defer free(t2)
@@ -64,21 +32,21 @@ poly_data_test :: proc(_t: ^testing.T) {
b_expect: [3]uintptr = 1 b_expect: [3]uintptr = 1
b2_expect: [MAX / 2]byte = 3 b2_expect: [MAX / 2]byte = 3
expect(poly_data_test_t, b == b_expect, "thread poly data not correct") testing.expect(poly_data_test_t, b == b_expect, "thread poly data not correct")
expect(poly_data_test_t, b2 == b2_expect, "thread poly data not correct") testing.expect(poly_data_test_t, b2 == b2_expect, "thread poly data not correct")
expect(poly_data_test_t, b3 == 333, "thread poly data not correct") testing.expect(poly_data_test_t, b3 == 333, "thread poly data not correct")
}) })
defer free(t3) defer free(t3)
t4 := thread.create_and_start_with_poly_data4(uintptr(111), b1, uintptr(333), u8(5), proc(n: uintptr, b: [3]uintptr, n2: uintptr, n4: u8) { t4 := thread.create_and_start_with_poly_data4(uintptr(111), b1, uintptr(333), u8(5), proc(n: uintptr, b: [3]uintptr, n2: uintptr, n4: u8) {
b_expect: [3]uintptr = 1 b_expect: [3]uintptr = 1
expect(poly_data_test_t, n == 111, "thread poly data not correct") testing.expect(poly_data_test_t, n == 111, "thread poly data not correct")
expect(poly_data_test_t, b == b_expect, "thread poly data not correct") testing.expect(poly_data_test_t, b == b_expect, "thread poly data not correct")
expect(poly_data_test_t, n2 == 333, "thread poly data not correct") testing.expect(poly_data_test_t, n2 == 333, "thread poly data not correct")
expect(poly_data_test_t, n4 == 5, "thread poly data not correct") testing.expect(poly_data_test_t, n4 == 5, "thread poly data not correct")
}) })
defer free(t4) defer free(t4)
thread.join_multiple(t1, t2, t3, t4) thread.join_multiple(t1, t2, t3, t4)
} }
+92 -78
View File
@@ -1,68 +1,17 @@
package test_core_time package test_core_time
import "core:fmt"
import "core:mem"
import "core:os"
import "core:testing" import "core:testing"
import "core:time" import "core:time"
import dt "core:time/datetime" import dt "core:time/datetime"
is_leap_year :: time.is_leap_year is_leap_year :: time.is_leap_year
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
expect_value :: testing.expect_value
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
main :: proc() {
t := testing.T{}
track: mem.Tracking_Allocator
mem.tracking_allocator_init(&track, context.allocator)
defer mem.tracking_allocator_destroy(&track)
context.allocator = mem.tracking_allocator(&track)
test_ordinal_date_roundtrip(&t)
test_component_to_time_roundtrip(&t)
test_parse_rfc3339_string(&t)
test_parse_iso8601_string(&t)
for _, leak in track.allocation_map {
expect(&t, false, fmt.tprintf("%v leaked %m\n", leak.location, leak.size))
}
for bad_free in track.bad_free_array {
expect(&t, false, fmt.tprintf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory))
}
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
@test @test
test_ordinal_date_roundtrip :: proc(t: ^testing.T) { test_ordinal_date_roundtrip :: proc(t: ^testing.T) {
expect(t, dt.unsafe_ordinal_to_date(dt.unsafe_date_to_ordinal(dt.MIN_DATE)) == dt.MIN_DATE, "Roundtripping MIN_DATE failed.") testing.expect(t, dt.unsafe_ordinal_to_date(dt.unsafe_date_to_ordinal(dt.MIN_DATE)) == dt.MIN_DATE, "Roundtripping MIN_DATE failed.")
expect(t, dt.unsafe_date_to_ordinal(dt.unsafe_ordinal_to_date(dt.MIN_ORD)) == dt.MIN_ORD, "Roundtripping MIN_ORD failed.") testing.expect(t, dt.unsafe_date_to_ordinal(dt.unsafe_ordinal_to_date(dt.MIN_ORD)) == dt.MIN_ORD, "Roundtripping MIN_ORD failed.")
expect(t, dt.unsafe_ordinal_to_date(dt.unsafe_date_to_ordinal(dt.MAX_DATE)) == dt.MAX_DATE, "Roundtripping MAX_DATE failed.") testing.expect(t, dt.unsafe_ordinal_to_date(dt.unsafe_date_to_ordinal(dt.MAX_DATE)) == dt.MAX_DATE, "Roundtripping MAX_DATE failed.")
expect(t, dt.unsafe_date_to_ordinal(dt.unsafe_ordinal_to_date(dt.MAX_ORD)) == dt.MAX_ORD, "Roundtripping MAX_ORD failed.") testing.expect(t, dt.unsafe_date_to_ordinal(dt.unsafe_ordinal_to_date(dt.MAX_ORD)) == dt.MAX_ORD, "Roundtripping MAX_ORD failed.")
} }
/* /*
@@ -160,22 +109,51 @@ test_parse_rfc3339_string :: proc(t: ^testing.T) {
is_leap := false is_leap := false
if test.apply_offset { if test.apply_offset {
res, consumed := time.rfc3339_to_time_utc(test.rfc_3339, &is_leap) res, consumed := time.rfc3339_to_time_utc(test.rfc_3339, &is_leap)
msg := fmt.tprintf("[apply offet] Parsing failed: %v -> %v (nsec: %v). Expected %v consumed, got %v", test.rfc_3339, res, res._nsec, test.consumed, consumed) testing.expectf(
expect(t, test.consumed == consumed, msg) t,
test.consumed == consumed,
"[apply offet] Parsing failed: %v -> %v (nsec: %v). Expected %v consumed, got %v",
test.rfc_3339, res, res._nsec, test.consumed, consumed,
)
if test.consumed == consumed { if test.consumed == consumed {
expect(t, test.datetime == res, fmt.tprintf("Time didn't match. Expected %v (%v), got %v (%v)", test.datetime, test.datetime._nsec, res, res._nsec)) testing.expectf(
expect(t, test.is_leap == is_leap, "Expected a leap second, got none.") t,
test.datetime == res,
"Time didn't match. Expected %v (%v), got %v (%v)",
test.datetime, test.datetime._nsec, res, res._nsec,
)
testing.expect(
t,
test.is_leap == is_leap,
"Expected a leap second, got none",
)
} }
} else { } else {
res, offset, consumed := time.rfc3339_to_time_and_offset(test.rfc_3339) res, offset, consumed := time.rfc3339_to_time_and_offset(test.rfc_3339)
msg := fmt.tprintf("Parsing failed: %v -> %v (nsec: %v), offset: %v. Expected %v consumed, got %v", test.rfc_3339, res, res._nsec, offset, test.consumed, consumed) testing.expectf(
expect(t, test.consumed == consumed, msg) t,
test.consumed == consumed,
"Parsing failed: %v -> %v (nsec: %v), offset: %v. Expected %v consumed, got %v",
test.rfc_3339, res, res._nsec, offset, test.consumed, consumed,
)
if test.consumed == consumed { if test.consumed == consumed {
expect(t, test.datetime == res, fmt.tprintf("Time didn't match. Expected %v (%v), got %v (%v)", test.datetime, test.datetime._nsec, res, res._nsec)) testing.expectf(
expect(t, test.utc_offset == offset, fmt.tprintf("UTC offset didn't match. Expected %v, got %v", test.utc_offset, offset)) t, test.datetime == res,
expect(t, test.is_leap == is_leap, "Expected a leap second, got none.") "Time didn't match. Expected %v (%v), got %v (%v)",
test.datetime, test.datetime._nsec, res, res._nsec,
)
testing.expectf(
t,
test.utc_offset == offset,
"UTC offset didn't match. Expected %v, got %v",
test.utc_offset, offset,
)
testing.expect(
t, test.is_leap == is_leap,
"Expected a leap second, got none",
)
} }
} }
} }
@@ -187,22 +165,52 @@ test_parse_iso8601_string :: proc(t: ^testing.T) {
is_leap := false is_leap := false
if test.apply_offset { if test.apply_offset {
res, consumed := time.iso8601_to_time_utc(test.iso_8601, &is_leap) res, consumed := time.iso8601_to_time_utc(test.iso_8601, &is_leap)
msg := fmt.tprintf("[apply offet] Parsing failed: %v -> %v (nsec: %v). Expected %v consumed, got %v", test.iso_8601, res, res._nsec, test.consumed, consumed) testing.expectf(
expect(t, test.consumed == consumed, msg) t,
test.consumed == consumed,
"[apply offet] Parsing failed: %v -> %v (nsec: %v). Expected %v consumed, got %v",
test.iso_8601, res, res._nsec, test.consumed, consumed,
)
if test.consumed == consumed { if test.consumed == consumed {
expect(t, test.datetime == res, fmt.tprintf("Time didn't match. Expected %v (%v), got %v (%v)", test.datetime, test.datetime._nsec, res, res._nsec)) testing.expectf(
expect(t, test.is_leap == is_leap, "Expected a leap second, got none.") t,
test.datetime == res,
"Time didn't match. Expected %v (%v), got %v (%v)",
test.datetime, test.datetime._nsec, res, res._nsec,
)
testing.expect(
t,
test.is_leap == is_leap,
"Expected a leap second, got none",
)
} }
} else { } else {
res, offset, consumed := time.iso8601_to_time_and_offset(test.iso_8601) res, offset, consumed := time.iso8601_to_time_and_offset(test.iso_8601)
msg := fmt.tprintf("Parsing failed: %v -> %v (nsec: %v), offset: %v. Expected %v consumed, got %v", test.iso_8601, res, res._nsec, offset, test.consumed, consumed) testing.expectf(
expect(t, test.consumed == consumed, msg) t,
test.consumed == consumed,
"Parsing failed: %v -> %v (nsec: %v), offset: %v. Expected %v consumed, got %v",
test.iso_8601, res, res._nsec, offset, test.consumed, consumed,
)
if test.consumed == consumed { if test.consumed == consumed {
expect(t, test.datetime == res, fmt.tprintf("Time didn't match. Expected %v (%v), got %v (%v)", test.datetime, test.datetime._nsec, res, res._nsec)) testing.expectf(
expect(t, test.utc_offset == offset, fmt.tprintf("UTC offset didn't match. Expected %v, got %v", test.utc_offset, offset)) t, test.datetime == res,
expect(t, test.is_leap == is_leap, "Expected a leap second, got none.") "Time didn't match. Expected %v (%v), got %v (%v)",
test.datetime, test.datetime._nsec, res, res._nsec,
)
testing.expectf(
t,
test.utc_offset == offset,
"UTC offset didn't match. Expected %v, got %v",
test.utc_offset, offset,
)
testing.expect(
t,
test.is_leap == is_leap,
"Expected a leap second, got none",
)
} }
} }
} }
@@ -231,15 +239,21 @@ test_component_to_time_roundtrip :: proc(t: ^testing.T) {
date_component_roundtrip_test :: proc(t: ^testing.T, moment: dt.DateTime) { date_component_roundtrip_test :: proc(t: ^testing.T, moment: dt.DateTime) {
res, ok := time.datetime_to_time(moment.year, moment.month, moment.day, moment.hour, moment.minute, moment.second) res, ok := time.datetime_to_time(moment.year, moment.month, moment.day, moment.hour, moment.minute, moment.second)
expect(t, ok, "Couldn't convert date components into date") testing.expect(
t,
ok,
"Couldn't convert date components into date",
)
YYYY, MM, DD := time.date(res) YYYY, MM, DD := time.date(res)
hh, mm, ss := time.clock(res) hh, mm, ss := time.clock(res)
expected := fmt.tprintf("Expected %4d-%2d-%2d %2d:%2d:%2d, got %4d-%2d-%2d %2d:%2d:%2d",
moment.year, moment.month, moment.day, moment.hour, moment.minute, moment.second, YYYY, MM, DD, hh, mm, ss)
ok = moment.year == i64(YYYY) && moment.month == i8(MM) && moment.day == i8(DD) ok = moment.year == i64(YYYY) && moment.month == i8(MM) && moment.day == i8(DD)
ok &= moment.hour == i8(hh) && moment.minute == i8(mm) && moment.second == i8(ss) ok &= moment.hour == i8(hh) && moment.minute == i8(mm) && moment.second == i8(ss)
expect(t, ok, expected) testing.expectf(
t,
ok,
"Expected %4d-%2d-%2d %2d:%2d:%2d, got %4d-%2d-%2d %2d:%2d:%2d",
moment.year, moment.month, moment.day, moment.hour, moment.minute, moment.second, YYYY, MM, DD, hh, mm, ss,
)
} }
+8 -9
View File
@@ -1,23 +1,22 @@
ODIN=../../odin ODIN=../../odin
COMMON=-file -vet -strict-style -o:minimal
all: all_bsd asan_test all: asan_test rtti_test map_test pow_test 128_test string_compare_test
all_bsd: rtti_test map_test pow_test 128_test string_compare_test
rtti_test: rtti_test:
$(ODIN) run test_rtti.odin -file -vet -strict-style -o:minimal $(ODIN) test test_rtti.odin $(COMMON)
map_test: map_test:
$(ODIN) run test_map.odin -file -vet -strict-style -o:minimal $(ODIN) test test_map.odin $(COMMON)
pow_test: pow_test:
$(ODIN) run test_pow.odin -file -vet -strict-style -o:minimal $(ODIN) test test_pow.odin $(COMMON)
128_test: 128_test:
$(ODIN) run test_128.odin -file -vet -strict-style -o:minimal $(ODIN) test test_128.odin $(COMMON)
asan_test: asan_test:
$(ODIN) run test_asan.odin -file -sanitize:address -debug $(ODIN) test test_asan.odin $(COMMON) -sanitize:address -debug
string_compare_test: string_compare_test:
$(ODIN) run test_string_compare.odin -file -vet -strict-style -o:minimal $(ODIN) test test_string_compare.odin $(COMMON)
+7 -8
View File
@@ -1,10 +1,9 @@
@echo off @echo off
set PATH_TO_ODIN==..\..\odin set PATH_TO_ODIN==..\..\odin
rem %PATH_TO_ODIN% run test_rtti.odin -file -vet -strict-style -o:minimal || exit /b set COMMON=-file -vet -strict-style -o:minimal
%PATH_TO_ODIN% run test_map.odin -file -vet -strict-style -o:minimal || exit /b %PATH_TO_ODIN% test test_rtti.odin %COMMON% || exit /b
rem -define:SEED=42 %PATH_TO_ODIN% test test_map.odin %COMMON% || exit /b
%PATH_TO_ODIN% run test_pow.odin -file -vet -strict-style -o:minimal || exit /b %PATH_TO_ODIN% test test_pow.odin %COMMON% || exit /b
%PATH_TO_ODIN% test test_asan.odin %COMMON% || exit /b
%PATH_TO_ODIN% run test_128.odin -file -vet -strict-style -o:minimal || exit /b %PATH_TO_ODIN% test test_128.odin %COMMON% || exit /b
%PATH_TO_ODIN% test test_string_compare.odin %COMMON% || exit /b
%PATH_TO_ODIN% run test_string_compare.odin -file -vet -strict-style -o:minimal || exit /b
+8 -42
View File
@@ -1,41 +1,7 @@
package test_128 package test_128
import "core:fmt"
import "core:os"
import "core:testing" import "core:testing"
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
main :: proc() {
t := testing.T{}
test_128_align(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
@test @test
test_128_align :: proc(t: ^testing.T) { test_128_align :: proc(t: ^testing.T) {
Danger_Struct :: struct { Danger_Struct :: struct {
@@ -45,15 +11,15 @@ test_128_align :: proc(t: ^testing.T) {
list := [?]Danger_Struct{{0, 0}, {1, 0}, {2, 0}, {3, 0}} list := [?]Danger_Struct{{0, 0}, {1, 0}, {2, 0}, {3, 0}}
expect(t, list[0].x == 0, fmt.tprintf("[0].x (%v) != 0", list[0].x)) testing.expectf(t, list[0].x == 0, "[0].x (%v) != 0", list[0].x)
expect(t, list[0].y == 0, fmt.tprintf("[0].y (%v) != 0", list[0].y)) testing.expectf(t, list[0].y == 0, "[0].y (%v) != 0", list[0].y)
expect(t, list[1].x == 1, fmt.tprintf("[1].x (%v) != 1", list[1].x)) testing.expectf(t, list[1].x == 1, "[1].x (%v) != 1", list[1].x)
expect(t, list[1].y == 0, fmt.tprintf("[1].y (%v) != 0", list[1].y)) testing.expectf(t, list[1].y == 0, "[1].y (%v) != 0", list[1].y)
expect(t, list[2].x == 2, fmt.tprintf("[2].x (%v) != 2", list[2].x)) testing.expectf(t, list[2].x == 2, "[2].x (%v) != 2", list[2].x)
expect(t, list[2].y == 0, fmt.tprintf("[2].y (%v) != 0", list[2].y)) testing.expectf(t, list[2].y == 0, "[2].y (%v) != 0", list[2].y)
expect(t, list[3].x == 3, fmt.tprintf("[3].x (%v) != 3", list[3].x)) testing.expectf(t, list[3].x == 3, "[3].x (%v) != 3", list[3].x)
expect(t, list[3].y == 0, fmt.tprintf("[3].y (%v) != 0", list[3].y)) testing.expectf(t, list[3].y == 0, "[3].y (%v) != 0", list[3].y)
} }
+5 -40
View File
@@ -1,42 +1,7 @@
// Intended to contain code that would trigger asan easily if the abi was set up badly. // Intended to contain code that would trigger asan easily if the abi was set up badly.
package test_asan package test_asan
import "core:fmt"
import "core:testing" import "core:testing"
import "core:os"
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
main :: proc() {
t := testing.T{}
test_12_bytes(&t)
test_12_bytes_two(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
@(test) @(test)
test_12_bytes :: proc(t: ^testing.T) { test_12_bytes :: proc(t: ^testing.T) {
@@ -45,9 +10,9 @@ test_12_bytes :: proc(t: ^testing.T) {
} }
a, b, ok := internal() a, b, ok := internal()
expect(t, a == max(f32), fmt.tprintf("a (%v) != max(f32)", a)) testing.expectf(t, a == max(f32), "a (%v) != max(f32)", a)
expect(t, b == 0, fmt.tprintf("b (%v) != 0", b)) testing.expectf(t, b == 0, "b (%v) != 0", b)
expect(t, ok, fmt.tprintf("ok (%v) != true", ok)) testing.expectf(t, ok, "ok (%v) != true", ok)
} }
@(test) @(test)
@@ -57,6 +22,6 @@ test_12_bytes_two :: proc(t: ^testing.T) {
} }
a, b := internal() a, b := internal()
expect(t, a == 100., fmt.tprintf("a (%v) != 100.", a)) testing.expectf(t, a == 100., "a (%v) != 100.", a)
expect(t, b == max(int), fmt.tprintf("b (%v) != max(int)", b)) testing.expectf(t, b == max(int), "b (%v) != max(int)", b)
} }
+46 -116
View File
@@ -1,26 +1,22 @@
package test_internal_map package test_internal_map
import "core:fmt" import "core:log"
import "base:intrinsics" import "base:intrinsics"
import "core:math/rand" import "core:math/rand"
import "core:mem"
import "core:os"
import "core:testing" import "core:testing"
seed: u64
ENTRY_COUNTS := []int{11, 101, 1_001, 10_001, 100_001, 1_000_001} ENTRY_COUNTS := []int{11, 101, 1_001, 10_001, 100_001, 1_000_001}
@test @test
map_insert_random_key_value :: proc(t: ^testing.T) { map_insert_random_key_value :: proc(t: ^testing.T) {
seed_incr := u64(0) seed_incr := u64(0)
for entries in ENTRY_COUNTS { for entries in ENTRY_COUNTS {
fmt.printf("[map_insert_random_key_value] Testing %v entries.\n", entries) log.infof("Testing %v entries", entries)
m: map[i64]i64 m: map[i64]i64
defer delete(m) defer delete(m)
unique_keys := 0 unique_keys := 0
r := rand.create(seed + seed_incr) r := rand.create(t.seed + seed_incr)
for _ in 0..<entries { for _ in 0..<entries {
k := rand.int63(&r) k := rand.int63(&r)
v := rand.int63(&r) v := rand.int63(&r)
@@ -36,11 +32,11 @@ map_insert_random_key_value :: proc(t: ^testing.T) {
key_count += 1 key_count += 1
} }
expect(t, key_count == unique_keys, fmt.tprintf("Expected key_count to equal %v, got %v", unique_keys, key_count)) testing.expectf(t, key_count == unique_keys, "Expected key_count to equal %v, got %v", unique_keys, key_count)
expect(t, len(m) == unique_keys, fmt.tprintf("Expected len(map) to equal %v, got %v", unique_keys, len(m))) testing.expectf(t, len(m) == unique_keys, "Expected len(map) to equal %v, got %v", unique_keys, len(m))
// Reset randomizer and verify // Reset randomizer and verify
r = rand.create(seed + seed_incr) r = rand.create(t.seed + seed_incr)
num_fails := 0 num_fails := 0
for _ in 0..<entries { for _ in 0..<entries {
@@ -51,10 +47,10 @@ map_insert_random_key_value :: proc(t: ^testing.T) {
if !cond { if !cond {
num_fails += 1 num_fails += 1
if num_fails > 5 { if num_fails > 5 {
fmt.println("... and more") log.info("... and more")
break break
} }
expect(t, false, fmt.tprintf("Unexpected value. Expected m[%v] = %v, got %v", k, v, m[k])) testing.expectf(t, false, "Unexpected value. Expected m[%v] = %v, got %v", k, v, m[k])
} }
} }
seed_incr += 1 seed_incr += 1
@@ -65,12 +61,12 @@ map_insert_random_key_value :: proc(t: ^testing.T) {
map_update_random_key_value :: proc(t: ^testing.T) { map_update_random_key_value :: proc(t: ^testing.T) {
seed_incr := u64(0) seed_incr := u64(0)
for entries in ENTRY_COUNTS { for entries in ENTRY_COUNTS {
fmt.printf("[map_update_random_key_value] Testing %v entries.\n", entries) log.infof("Testing %v entries", entries)
m: map[i64]i64 m: map[i64]i64
defer delete(m) defer delete(m)
unique_keys := 0 unique_keys := 0
r := rand.create(seed + seed_incr) r := rand.create(t.seed + seed_incr)
for _ in 0..<entries { for _ in 0..<entries {
k := rand.int63(&r) k := rand.int63(&r)
v := rand.int63(&r) v := rand.int63(&r)
@@ -86,13 +82,13 @@ map_update_random_key_value :: proc(t: ^testing.T) {
key_count += 1 key_count += 1
} }
expect(t, key_count == unique_keys, fmt.tprintf("Expected key_count to equal %v, got %v", unique_keys, key_count)) testing.expectf(t, key_count == unique_keys, "Expected key_count to equal %v, got %v", unique_keys, key_count)
expect(t, len(m) == unique_keys, fmt.tprintf("Expected len(map) to equal %v, got %v", unique_keys, len(m))) testing.expectf(t, len(m) == unique_keys, "Expected len(map) to equal %v, got %v", unique_keys, len(m))
half_entries := entries / 2 half_entries := entries / 2
// Reset randomizer and update half the entries // Reset randomizer and update half the entries
r = rand.create(seed + seed_incr) r = rand.create(t.seed + seed_incr)
for _ in 0..<half_entries { for _ in 0..<half_entries {
k := rand.int63(&r) k := rand.int63(&r)
v := rand.int63(&r) v := rand.int63(&r)
@@ -101,7 +97,7 @@ map_update_random_key_value :: proc(t: ^testing.T) {
} }
// Reset randomizer and verify // Reset randomizer and verify
r = rand.create(seed + seed_incr) r = rand.create(t.seed + seed_incr)
num_fails := 0 num_fails := 0
for i in 0..<entries { for i in 0..<entries {
@@ -113,10 +109,10 @@ map_update_random_key_value :: proc(t: ^testing.T) {
if !cond { if !cond {
num_fails += 1 num_fails += 1
if num_fails > 5 { if num_fails > 5 {
fmt.println("... and more") log.info("... and more")
break break
} }
expect(t, false, fmt.tprintf("Unexpected value. Expected m[%v] = %v, got %v", k, v, m[k])) testing.expectf(t, false, "Unexpected value. Expected m[%v] = %v, got %v", k, v, m[k])
} }
} }
seed_incr += 1 seed_incr += 1
@@ -127,12 +123,12 @@ map_update_random_key_value :: proc(t: ^testing.T) {
map_delete_random_key_value :: proc(t: ^testing.T) { map_delete_random_key_value :: proc(t: ^testing.T) {
seed_incr := u64(0) seed_incr := u64(0)
for entries in ENTRY_COUNTS { for entries in ENTRY_COUNTS {
fmt.printf("[map_delete_random_key_value] Testing %v entries.\n", entries) log.infof("Testing %v entries", entries)
m: map[i64]i64 m: map[i64]i64
defer delete(m) defer delete(m)
unique_keys := 0 unique_keys := 0
r := rand.create(seed + seed_incr) r := rand.create(t.seed + seed_incr)
for _ in 0..<entries { for _ in 0..<entries {
k := rand.int63(&r) k := rand.int63(&r)
v := rand.int63(&r) v := rand.int63(&r)
@@ -148,13 +144,13 @@ map_delete_random_key_value :: proc(t: ^testing.T) {
key_count += 1 key_count += 1
} }
expect(t, key_count == unique_keys, fmt.tprintf("Expected key_count to equal %v, got %v", unique_keys, key_count)) testing.expectf(t, key_count == unique_keys, "Expected key_count to equal %v, got %v", unique_keys, key_count)
expect(t, len(m) == unique_keys, fmt.tprintf("Expected len(map) to equal %v, got %v", unique_keys, len(m))) testing.expectf(t, len(m) == unique_keys, "Expected len(map) to equal %v, got %v", unique_keys, len(m))
half_entries := entries / 2 half_entries := entries / 2
// Reset randomizer and delete half the entries // Reset randomizer and delete half the entries
r = rand.create(seed + seed_incr) r = rand.create(t.seed + seed_incr)
for _ in 0..<half_entries { for _ in 0..<half_entries {
k := rand.int63(&r) k := rand.int63(&r)
_ = rand.int63(&r) _ = rand.int63(&r)
@@ -163,7 +159,7 @@ map_delete_random_key_value :: proc(t: ^testing.T) {
} }
// Reset randomizer and verify // Reset randomizer and verify
r = rand.create(seed + seed_incr) r = rand.create(t.seed + seed_incr)
num_fails := 0 num_fails := 0
for i in 0..<entries { for i in 0..<entries {
@@ -174,26 +170,26 @@ map_delete_random_key_value :: proc(t: ^testing.T) {
if k in m { if k in m {
num_fails += 1 num_fails += 1
if num_fails > 5 { if num_fails > 5 {
fmt.println("... and more") log.info("... and more")
break break
} }
expect(t, false, fmt.tprintf("Unexpected key present. Expected m[%v] to have been deleted, got %v", k, m[k])) testing.expectf(t, false, "Unexpected key present. Expected m[%v] to have been deleted, got %v", k, m[k])
} }
} else { } else {
if k not_in m { if k not_in m {
num_fails += 1 num_fails += 1
if num_fails > 5 { if num_fails > 5 {
fmt.println("... and more") log.info("... and more")
break break
} }
expect(t, false, fmt.tprintf("Expected key not present. Expected m[%v] = %v", k, v)) testing.expectf(t, false, "Expected key not present. Expected m[%v] = %v", k, v)
} else if m[k] != v { } else if m[k] != v {
num_fails += 1 num_fails += 1
if num_fails > 5 { if num_fails > 5 {
fmt.println("... and more") log.info("... and more")
break break
} }
expect(t, false, fmt.tprintf("Unexpected value. Expected m[%v] = %v, got %v", k, v, m[k])) testing.expectf(t, false, "Unexpected value. Expected m[%v] = %v, got %v", k, v, m[k])
} }
} }
} }
@@ -205,12 +201,12 @@ map_delete_random_key_value :: proc(t: ^testing.T) {
set_insert_random_key_value :: proc(t: ^testing.T) { set_insert_random_key_value :: proc(t: ^testing.T) {
seed_incr := u64(0) seed_incr := u64(0)
for entries in ENTRY_COUNTS { for entries in ENTRY_COUNTS {
fmt.printf("[set_insert_random_key_value] Testing %v entries.\n", entries) log.infof("Testing %v entries", entries)
m: map[i64]struct{} m: map[i64]struct{}
defer delete(m) defer delete(m)
unique_keys := 0 unique_keys := 0
r := rand.create(seed + seed_incr) r := rand.create(t.seed + seed_incr)
for _ in 0..<entries { for _ in 0..<entries {
k := rand.int63(&r) k := rand.int63(&r)
if k not_in m { if k not_in m {
@@ -224,11 +220,11 @@ set_insert_random_key_value :: proc(t: ^testing.T) {
key_count += 1 key_count += 1
} }
expect(t, key_count == unique_keys, fmt.tprintf("Expected key_count to equal %v, got %v", unique_keys, key_count)) testing.expectf(t, key_count == unique_keys, "Expected key_count to equal %v, got %v", unique_keys, key_count)
expect(t, len(m) == unique_keys, fmt.tprintf("Expected len(map) to equal %v, got %v", unique_keys, len(m))) testing.expectf(t, len(m) == unique_keys, "Expected len(map) to equal %v, got %v", unique_keys, len(m))
// Reset randomizer and verify // Reset randomizer and verify
r = rand.create(seed + seed_incr) r = rand.create(t.seed + seed_incr)
num_fails := 0 num_fails := 0
for _ in 0..<entries { for _ in 0..<entries {
@@ -238,10 +234,10 @@ set_insert_random_key_value :: proc(t: ^testing.T) {
if !cond { if !cond {
num_fails += 1 num_fails += 1
if num_fails > 5 { if num_fails > 5 {
fmt.println("... and more") log.info("... and more")
break break
} }
expect(t, false, fmt.tprintf("Unexpected value. Expected m[%v] to exist", k)) testing.expectf(t, false, "Unexpected value. Expected m[%v] to exist", k)
} }
} }
seed_incr += 1 seed_incr += 1
@@ -252,12 +248,12 @@ set_insert_random_key_value :: proc(t: ^testing.T) {
set_delete_random_key_value :: proc(t: ^testing.T) { set_delete_random_key_value :: proc(t: ^testing.T) {
seed_incr := u64(0) seed_incr := u64(0)
for entries in ENTRY_COUNTS { for entries in ENTRY_COUNTS {
fmt.printf("[set_delete_random_key_value] Testing %v entries.\n", entries) log.infof("Testing %v entries", entries)
m: map[i64]struct{} m: map[i64]struct{}
defer delete(m) defer delete(m)
unique_keys := 0 unique_keys := 0
r := rand.create(seed + seed_incr) r := rand.create(t.seed + seed_incr)
for _ in 0..<entries { for _ in 0..<entries {
k := rand.int63(&r) k := rand.int63(&r)
@@ -272,20 +268,20 @@ set_delete_random_key_value :: proc(t: ^testing.T) {
key_count += 1 key_count += 1
} }
expect(t, key_count == unique_keys, fmt.tprintf("Expected key_count to equal %v, got %v", unique_keys, key_count)) testing.expectf(t, key_count == unique_keys, "Expected key_count to equal %v, got %v", unique_keys, key_count)
expect(t, len(m) == unique_keys, fmt.tprintf("Expected len(map) to equal %v, got %v", unique_keys, len(m))) testing.expectf(t, len(m) == unique_keys, "Expected len(map) to equal %v, got %v", unique_keys, len(m))
half_entries := entries / 2 half_entries := entries / 2
// Reset randomizer and delete half the entries // Reset randomizer and delete half the entries
r = rand.create(seed + seed_incr) r = rand.create(t.seed + seed_incr)
for _ in 0..<half_entries { for _ in 0..<half_entries {
k := rand.int63(&r) k := rand.int63(&r)
delete_key(&m, k) delete_key(&m, k)
} }
// Reset randomizer and verify // Reset randomizer and verify
r = rand.create(seed + seed_incr) r = rand.create(t.seed + seed_incr)
num_fails := 0 num_fails := 0
for i in 0..<entries { for i in 0..<entries {
@@ -295,88 +291,22 @@ set_delete_random_key_value :: proc(t: ^testing.T) {
if k in m { if k in m {
num_fails += 1 num_fails += 1
if num_fails > 5 { if num_fails > 5 {
fmt.println("... and more") log.info("... and more")
break break
} }
expect(t, false, fmt.tprintf("Unexpected key present. Expected m[%v] to have been deleted", k)) testing.expectf(t, false, "Unexpected key present. Expected m[%v] to have been deleted", k)
} }
} else { } else {
if k not_in m { if k not_in m {
num_fails += 1 num_fails += 1
if num_fails > 5 { if num_fails > 5 {
fmt.println("... and more") log.info("... and more")
break break
} }
expect(t, false, fmt.tprintf("Expected key not present. Expected m[%v] to exist", k)) testing.expectf(t, false, "Expected key not present. Expected m[%v] to exist", k)
} }
} }
} }
seed_incr += 1 seed_incr += 1
} }
} }
// -------- -------- -------- -------- -------- -------- -------- -------- -------- --------
main :: proc() {
t := testing.T{}
// Allow tests to be repeatable
SEED :: #config(SEED, -1)
when SEED > 0 {
seed = u64(SEED)
} else {
seed = u64(intrinsics.read_cycle_counter())
}
fmt.println("Initialized seed to", seed)
mem_track_test(&t, map_insert_random_key_value)
mem_track_test(&t, map_update_random_key_value)
mem_track_test(&t, map_delete_random_key_value)
mem_track_test(&t, set_insert_random_key_value)
mem_track_test(&t, set_delete_random_key_value)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
mem_track_test :: proc(t: ^testing.T, test: proc(t: ^testing.T)) {
track: mem.Tracking_Allocator
mem.tracking_allocator_init(&track, context.allocator)
context.allocator = mem.tracking_allocator(&track)
test(t)
expect(t, len(track.allocation_map) == 0, "Expected no leaks.")
expect(t, len(track.bad_free_array) == 0, "Expected no leaks.")
for _, leak in track.allocation_map {
fmt.printf("%v leaked %v bytes\n", leak.location, leak.size)
}
for bad_free in track.bad_free_array {
fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory)
}
}
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
+6 -42
View File
@@ -1,8 +1,7 @@
package test_internal_math_pow package test_internal_math_pow
import "core:fmt" @(require) import "core:log"
import "core:math" import "core:math"
import "core:os"
import "core:testing" import "core:testing"
@test @test
@@ -19,14 +18,14 @@ pow_test :: proc(t: ^testing.T) {
// pow2_f64 returns the same float on all platforms because it isn't this stupid // pow2_f64 returns the same float on all platforms because it isn't this stupid
_v1 = 0h00000000_00000000 _v1 = 0h00000000_00000000
} }
expect(t, _v1 == _v2, fmt.tprintf("Expected math.pow2_f64(%d) == math.pow(2, %d) (= %16x), got %16x", exp, exp, _v1, _v2)) testing.expectf(t, _v1 == _v2, "Expected math.pow2_f64(%d) == math.pow(2, %d) (= %16x), got %16x", exp, exp, _v1, _v2)
} }
{ {
v1 := math.pow(2, f32(exp)) v1 := math.pow(2, f32(exp))
v2 := math.pow2_f32(exp) v2 := math.pow2_f32(exp)
_v1 := transmute(u32)v1 _v1 := transmute(u32)v1
_v2 := transmute(u32)v2 _v2 := transmute(u32)v2
expect(t, _v1 == _v2, fmt.tprintf("Expected math.pow2_f32(%d) == math.pow(2, %d) (= %08x), got %08x", exp, exp, _v1, _v2)) testing.expectf(t, _v1 == _v2, "Expected math.pow2_f32(%d) == math.pow(2, %d) (= %08x), got %08x", exp, exp, _v1, _v2)
} }
{ {
v1 := math.pow(2, f16(exp)) v1 := math.pow(2, f16(exp))
@@ -36,46 +35,11 @@ pow_test :: proc(t: ^testing.T) {
when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 { when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 {
if exp == -25 { if exp == -25 {
testing.logf(t, "skipping known test failure on darwin+arm64, Expected math.pow2_f16(-25) == math.pow(2, -25) (= 0000), got 0001") log.info("skipping known test failure on darwin+arm64, Expected math.pow2_f16(-25) == math.pow(2, -25) (= 0000), got 0001")
_v2 = 0 _v2 = 0
} }
} }
testing.expectf(t, _v1 == _v2, "Expected math.pow2_f16(%d) == math.pow(2, %d) (= %04x), got %04x", exp, exp, _v1, _v2)
expect(t, _v1 == _v2, fmt.tprintf("Expected math.pow2_f16(%d) == math.pow(2, %d) (= %04x), got %04x", exp, exp, _v1, _v2))
} }
} }
} }
// -------- -------- -------- -------- -------- -------- -------- -------- -------- --------
main :: proc() {
t := testing.T{}
pow_test(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
+7 -62
View File
@@ -1,11 +1,8 @@
package test_internal_rtti package test_internal_rtti
import "core:fmt" import "core:fmt"
import "core:mem"
import "core:os"
import "core:testing" import "core:testing"
Buggy_Struct :: struct { Buggy_Struct :: struct {
a: int, a: int,
b: bool, b: bool,
@@ -28,74 +25,22 @@ rtti_test :: proc(t: ^testing.T) {
for v, i in g_b { for v, i in g_b {
checksum += (i+1) * int(v) checksum += (i+1) * int(v)
} }
expect(t, checksum == 0, fmt.tprintf("Expected g_b to be zero-initialized, got %v", g_b)) testing.expectf(t, checksum == 0, "Expected g_b to be zero-initialized, got %v", g_b)
} }
{ {
checksum := 0 checksum := 0
for v, i in l_b { for v, i in l_b {
checksum += (i+1) * int(v) checksum += (i+1) * int(v)
} }
expect(t, checksum == 0, fmt.tprintf("Expected l_b to be zero-initialized, got %v", l_b)) testing.expectf(t, checksum == 0, "Expected l_b to be zero-initialized, got %v", l_b)
} }
expect(t, size_of(Buggy_Struct) == 40, fmt.tprintf("Expected size_of(Buggy_Struct) == 40, got %v", size_of(Buggy_Struct))) testing.expectf(t, size_of(Buggy_Struct) == 40, "Expected size_of(Buggy_Struct) == 40, got %v", size_of(Buggy_Struct))
expect(t, size_of(g_buggy) == 40, fmt.tprintf("Expected size_of(g_buggy) == 40, got %v", size_of(g_buggy))) testing.expectf(t, size_of(g_buggy) == 40, "Expected size_of(g_buggy) == 40, got %v", size_of(g_buggy))
expect(t, size_of(l_buggy) == 40, fmt.tprintf("Expected size_of(l_buggy) == 40, got %v", size_of(l_buggy))) testing.expectf(t, size_of(l_buggy) == 40, "Expected size_of(l_buggy) == 40, got %v", size_of(l_buggy))
g_s := fmt.tprintf("%s", g_buggy) g_s := fmt.tprintf("%s", g_buggy)
l_s := fmt.tprintf("%s", l_buggy) l_s := fmt.tprintf("%s", l_buggy)
expect(t, g_s == EXPECTED_REPR, fmt.tprintf("Expected fmt.tprintf(\"%%s\", g_s)) to return \"%v\", got \"%v\"", EXPECTED_REPR, g_s)) testing.expectf(t, g_s == EXPECTED_REPR, "Expected fmt.tprintf(\"%%s\", g_s)) to return \"%v\", got \"%v\"", EXPECTED_REPR, g_s)
expect(t, l_s == EXPECTED_REPR, fmt.tprintf("Expected fmt.tprintf(\"%%s\", l_s)) to return \"%v\", got \"%v\"", EXPECTED_REPR, l_s)) testing.expectf(t, l_s == EXPECTED_REPR, "Expected fmt.tprintf(\"%%s\", l_s)) to return \"%v\", got \"%v\"", EXPECTED_REPR, l_s)
}
// -------- -------- -------- -------- -------- -------- -------- -------- -------- --------
main :: proc() {
t := testing.T{}
mem_track_test(&t, rtti_test)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
mem_track_test :: proc(t: ^testing.T, test: proc(t: ^testing.T)) {
track: mem.Tracking_Allocator
mem.tracking_allocator_init(&track, context.allocator)
context.allocator = mem.tracking_allocator(&track)
test(t)
expect(t, len(track.allocation_map) == 0, "Expected no leaks.")
expect(t, len(track.bad_free_array) == 0, "Expected no leaks.")
for _, leak in track.allocation_map {
fmt.printf("%v leaked %v bytes\n", leak.location, leak.size)
}
for bad_free in track.bad_free_array {
fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory)
}
}
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
} }
+12 -48
View File
@@ -1,7 +1,5 @@
package test_internal_string_compare package test_internal_string_compare
import "core:fmt"
import "core:os"
import "core:testing" import "core:testing"
Op :: enum { Eq, Lt, Gt } Op :: enum { Eq, Lt, Gt }
@@ -29,65 +27,31 @@ string_compare :: proc(t: ^testing.T) {
for res, op in v.res { for res, op in v.res {
switch op { switch op {
case .Eq: case .Eq:
expect(t, (v.a == v.b) == res, fmt.tprintf("Expected cstring(\"%v\") == cstring(\"%v\") to be %v", v.a, v.b, res)) testing.expectf(t, (v.a == v.b) == res, "Expected cstring(\"%v\") == cstring(\"%v\") to be %v", v.a, v.b, res)
expect(t, (s_a == s_b) == res, fmt.tprintf("Expected string(\"%v\") == string(\"%v\") to be %v", v.a, v.b, res)) testing.expectf(t, (s_a == s_b) == res, "Expected string(\"%v\") == string(\"%v\") to be %v", v.a, v.b, res)
// If a == b then a != b // If a == b then a != b
expect(t, (v.a != v.b) == !res, fmt.tprintf("Expected cstring(\"%v\") != cstring(\"%v\") to be %v", v.a, v.b, !res)) testing.expectf(t, (v.a != v.b) == !res, "Expected cstring(\"%v\") != cstring(\"%v\") to be %v", v.a, v.b, !res)
expect(t, (s_a != s_b) == !res, fmt.tprintf("Expected string(\"%v\") != string(\"%v\") to be %v", v.a, v.b, !res)) testing.expectf(t, (s_a != s_b) == !res, "Expected string(\"%v\") != string(\"%v\") to be %v", v.a, v.b, !res)
case .Lt: case .Lt:
expect(t, (v.a < v.b) == res, fmt.tprintf("Expected cstring(\"%v\") < cstring(\"%v\") to be %v", v.a, v.b, res)) testing.expectf(t, (v.a < v.b) == res, "Expected cstring(\"%v\") < cstring(\"%v\") to be %v", v.a, v.b, res)
expect(t, (s_a < s_b) == res, fmt.tprintf("Expected string(\"%v\") < string(\"%v\") to be %v", v.a, v.b, res)) testing.expectf(t, (s_a < s_b) == res, "Expected string(\"%v\") < string(\"%v\") to be %v", v.a, v.b, res)
// .Lt | .Eq == .LtEq // .Lt | .Eq == .LtEq
lteq := v.res[.Eq] | res lteq := v.res[.Eq] | res
expect(t, (v.a <= v.b) == lteq, fmt.tprintf("Expected cstring(\"%v\") <= cstring(\"%v\") to be %v", v.a, v.b, lteq)) testing.expectf(t, (v.a <= v.b) == lteq, "Expected cstring(\"%v\") <= cstring(\"%v\") to be %v", v.a, v.b, lteq)
expect(t, (s_a <= s_b) == lteq, fmt.tprintf("Expected string(\"%v\") <= string(\"%v\") to be %v", v.a, v.b, lteq)) testing.expectf(t, (s_a <= s_b) == lteq, "Expected string(\"%v\") <= string(\"%v\") to be %v", v.a, v.b, lteq)
case .Gt: case .Gt:
expect(t, (v.a > v.b) == res, fmt.tprintf("Expected cstring(\"%v\") > cstring(\"%v\") to be %v", v.a, v.b, res)) testing.expectf(t, (v.a > v.b) == res, "Expected cstring(\"%v\") > cstring(\"%v\") to be %v", v.a, v.b, res)
expect(t, (s_a > s_b) == res, fmt.tprintf("Expected string(\"%v\") > string(\"%v\") to be %v", v.a, v.b, res)) testing.expectf(t, (s_a > s_b) == res, "Expected string(\"%v\") > string(\"%v\") to be %v", v.a, v.b, res)
// .Gt | .Eq == .GtEq // .Gt | .Eq == .GtEq
gteq := v.res[.Eq] | res gteq := v.res[.Eq] | res
expect(t, (v.a >= v.b) == gteq, fmt.tprintf("Expected cstring(\"%v\") >= cstring(\"%v\") to be %v", v.a, v.b, gteq)) testing.expectf(t, (v.a >= v.b) == gteq, "Expected cstring(\"%v\") >= cstring(\"%v\") to be %v", v.a, v.b, gteq)
expect(t, (s_a >= s_b) == gteq, fmt.tprintf("Expected string(\"%v\") >= string(\"%v\") to be %v", v.a, v.b, gteq)) testing.expectf(t, (s_a >= s_b) == gteq, "Expected string(\"%v\") >= string(\"%v\") to be %v", v.a, v.b, gteq)
} }
} }
} }
}
// -------- -------- -------- -------- -------- -------- -------- -------- -------- --------
main :: proc() {
t := testing.T{}
string_compare(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
} }

Some files were not shown because too many files have changed in this diff Show More