diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ffb2077d15e..c5493775fee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,6 +34,7 @@ jobs: (cd tests/core; gmake all_bsd) (cd tests/internal; gmake all_bsd) (cd tests/issues; ./run.sh) + (cd tests/benchmark; gmake all) build_linux: name: Ubuntu Build, Check, and Test runs-on: ubuntu-latest @@ -80,6 +81,11 @@ jobs: cd tests/internal make timeout-minutes: 10 + - name: Odin core library benchmarks + run: | + cd tests/benchmark + make + timeout-minutes: 10 - name: Odin check examples/all for Linux i386 run: ./odin check examples/all -vet -strict-style -target:linux_i386 timeout-minutes: 10 @@ -131,6 +137,11 @@ jobs: cd tests/internal make timeout-minutes: 10 + - name: Odin core library benchmarks + run: | + cd tests/benchmark + make + timeout-minutes: 10 build_macOS_arm: name: MacOS ARM Build, Check, and Test runs-on: macos-14 # This is an arm/m1 runner. @@ -170,6 +181,11 @@ jobs: cd tests/internal make timeout-minutes: 10 + - name: Odin core library benchmarks + run: | + cd tests/benchmark + make + timeout-minutes: 10 build_windows: name: Windows Build, Check, and Test runs-on: windows-2022 @@ -217,6 +233,13 @@ jobs: cd tests\core call build.bat 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 shell: cmd run: | diff --git a/core/bufio/reader.odin b/core/bufio/reader.odin index 8ec736a6696..a875c732d7c 100644 --- a/core/bufio/reader.odin +++ b/core/bufio/reader.odin @@ -29,12 +29,12 @@ MIN_READ_BUFFER_SIZE :: 16 @(private) 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 = max(size, MIN_READ_BUFFER_SIZE) reader_reset(b, rd) 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) { diff --git a/core/bytes/buffer.odin b/core/bytes/buffer.odin index cb2ef9c62a4..a7e9b1c6424 100644 --- a/core/bytes/buffer.odin +++ b/core/bytes/buffer.odin @@ -27,19 +27,19 @@ Read_Op :: enum i8 { } -buffer_init :: proc(b: ^Buffer, buf: []byte) { - resize(&b.buf, len(buf)) +buffer_init :: proc(b: ^Buffer, buf: []byte, loc := #caller_location) { + resize(&b.buf, len(buf), loc=loc) copy(b.buf[:], buf) } -buffer_init_string :: proc(b: ^Buffer, s: string) { - resize(&b.buf, len(s)) +buffer_init_string :: proc(b: ^Buffer, s: string, loc := #caller_location) { + resize(&b.buf, len(s), loc=loc) 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 { - b.buf = make([dynamic]byte, len, cap, allocator) + b.buf = make([dynamic]byte, len, cap, allocator, loc) return } @@ -96,28 +96,28 @@ buffer_truncate :: proc(b: ^Buffer, n: int) { } @(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 { - resize(&b.buf, l+n) + resize(&b.buf, l+n, loc=loc) return l, true } return 0, false } @(private) -_buffer_grow :: proc(b: ^Buffer, n: int) -> int { +_buffer_grow :: proc(b: ^Buffer, n: int, loc := #caller_location) -> int { m := buffer_length(b) if m == 0 && b.off != 0 { 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 } if b.buf == nil && n <= SMALL_BUFFER_SIZE { // Fixes #2756 by preserving allocator if already set on Buffer via init_buffer_allocator - reserve(&b.buf, SMALL_BUFFER_SIZE) - resize(&b.buf, n) + reserve(&b.buf, SMALL_BUFFER_SIZE, loc=loc) + resize(&b.buf, n, loc=loc) return 0 } @@ -127,31 +127,31 @@ _buffer_grow :: proc(b: ^Buffer, n: int) -> int { } else if c > max(int) - c - n { panic("bytes.Buffer: too large") } else { - resize(&b.buf, 2*c + n) + resize(&b.buf, 2*c + n, loc=loc) copy(b.buf[:], b.buf[b.off:]) } b.off = 0 - resize(&b.buf, m+n) + resize(&b.buf, m+n, loc=loc) return m } -buffer_grow :: proc(b: ^Buffer, n: int) { +buffer_grow :: proc(b: ^Buffer, n: int, loc := #caller_location) { if n < 0 { panic("bytes.buffer_grow: negative count") } - m := _buffer_grow(b, n) - resize(&b.buf, m) + m := _buffer_grow(b, n, loc=loc) + 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 if offset < 0 { err = .Invalid_Offset return } - _, ok := _buffer_try_grow(b, offset+len(p)) + _, ok := _buffer_try_grow(b, offset+len(p), loc=loc) if !ok { - _ = _buffer_grow(b, offset+len(p)) + _ = _buffer_grow(b, offset+len(p), loc=loc) } if len(b.buf) <= offset { 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 - m, ok := _buffer_try_grow(b, len(p)) + m, ok := _buffer_try_grow(b, len(p), loc=loc) if !ok { - m = _buffer_grow(b, len(p)) + m = _buffer_grow(b, len(p), loc=loc) } return copy(b.buf[m:], p), nil } -buffer_write_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.Error) { - return buffer_write(b, ([^]byte)(ptr)[:size]) +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], 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 - m, ok := _buffer_try_grow(b, len(s)) + m, ok := _buffer_try_grow(b, len(s), loc=loc) if !ok { - m = _buffer_grow(b, len(s)) + m = _buffer_grow(b, len(s), loc=loc) } 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 - m, ok := _buffer_try_grow(b, 1) + m, ok := _buffer_try_grow(b, 1, loc=loc) if !ok { - m = _buffer_grow(b, 1) + m = _buffer_grow(b, 1, loc=loc) } b.buf[m] = c 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 { - buffer_write_byte(b, byte(r)) + buffer_write_byte(b, byte(r), loc=loc) return 1, nil } 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 { - m = _buffer_grow(b, utf8.UTF_MAX) + m = _buffer_grow(b, utf8.UTF_MAX, loc=loc) } res: [4]byte res, n = utf8.encode_rune(r) diff --git a/core/c/libc/signal.odin b/core/c/libc/signal.odin index 186b74d8c4e..1489779fec1 100644 --- a/core/c/libc/signal.odin +++ b/core/c/libc/signal.odin @@ -34,7 +34,7 @@ when ODIN_OS == .Windows { 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_DFL :: rawptr(uintptr(0)) SIG_IGN :: rawptr(uintptr(1)) diff --git a/core/c/libc/stdio.odin b/core/c/libc/stdio.odin index f17d3bd0677..3e1d0f5a252 100644 --- a/core/c/libc/stdio.odin +++ b/core/c/libc/stdio.odin @@ -102,10 +102,12 @@ when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD { SEEK_END :: 2 foreign libc { - stderr: ^FILE - stdin: ^FILE - stdout: ^FILE + __sF: [3]FILE } + + stdin: ^FILE = &__sF[0] + stdout: ^FILE = &__sF[1] + stderr: ^FILE = &__sF[2] } when ODIN_OS == .FreeBSD { @@ -127,9 +129,9 @@ when ODIN_OS == .FreeBSD { SEEK_END :: 2 foreign libc { - stderr: ^FILE - stdin: ^FILE - stdout: ^FILE + @(link_name="__stderrp") stderr: ^FILE + @(link_name="__stdinp") stdin: ^FILE + @(link_name="__stdoutp") stdout: ^FILE } } diff --git a/core/encoding/ansi/ansi.odin b/core/encoding/ansi/ansi.odin new file mode 100644 index 00000000000..5550a1671bb --- /dev/null +++ b/core/encoding/ansi/ansi.odin @@ -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 ";" ST. +HYPERLINK :: "8" // Followed by ";[params];" ST. Closed by OSC HYPERLINK ";;" ST. +CLIPBOARD :: "52" // Followed by ";c;" ST. diff --git a/core/encoding/ansi/doc.odin b/core/encoding/ansi/doc.odin new file mode 100644 index 00000000000..a0945c58189 --- /dev/null +++ b/core/encoding/ansi/doc.odin @@ -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 diff --git a/core/encoding/cbor/cbor.odin b/core/encoding/cbor/cbor.odin index d0e406ab1eb..7897b2a3743 100644 --- a/core/encoding/cbor/cbor.odin +++ b/core/encoding/cbor/cbor.odin @@ -320,8 +320,8 @@ to_diagnostic_format :: proc { // Turns the given CBOR value into a human-readable string. // 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 { - b := strings.builder_make(allocator) +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, loc) w := strings.to_stream(&b) err := to_diagnostic_format_writer(w, val, padding) if err == .EOF { diff --git a/core/encoding/cbor/coding.odin b/core/encoding/cbor/coding.odin index 0d276a7a1c1..07f0637a693 100644 --- a/core/encoding/cbor/coding.odin +++ b/core/encoding/cbor/coding.odin @@ -95,24 +95,25 @@ decode :: decode_from // Decodes the given string as CBOR. // 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 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. // 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( Decoder{ DEFAULT_MAX_PRE_ALLOC, flags, r }, allocator=allocator, + loc = loc, ) } // Reads a CBOR value from the given decoder. // 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 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 } - 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. if err == .EOF { err = .Unexpected_EOF } 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 r := d.reader 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 { case .Unsigned: return _decode_tiny_u8(add) case .Negative: return Negative_U8(_decode_tiny_u8(add) or_return), nil - case .Bytes: return _decode_bytes_ptr(d, add) - case .Text: return _decode_text_ptr(d, add) - case .Array: return _decode_array_ptr(d, add) - case .Map: return _decode_map_ptr(d, add) - case .Tag: return _decode_tag_ptr(d, add) + case .Bytes: return _decode_bytes_ptr(d, add, .Bytes, allocator, loc) + case .Text: return _decode_text_ptr(d, add, allocator, loc) + case .Array: return _decode_array_ptr(d, add, allocator, loc) + case .Map: return _decode_map_ptr(d, add, allocator, loc) + case .Tag: return _decode_tag_ptr(d, add, allocator, loc) case .Other: return _decode_tiny_simple(add) case: return nil, .Bad_Major } @@ -203,27 +204,27 @@ encode :: encode_into // Encodes the CBOR value into binary CBOR allocated on the given allocator. // 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) { - b := strings.builder_make(allocator) or_return +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, loc) or_return encode_into_builder(&b, v, flags, temp_allocator) or_return return b.buf[:], nil } // Encodes the CBOR value into binary CBOR written to the given builder. // 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 { - return encode_into_writer(strings.to_stream(b), v, flags, temp_allocator) +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, loc=loc) } // Encodes the CBOR value into binary CBOR written to the given writer. // 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 { - return encode_into_encoder(Encoder{flags, w, temp_allocator}, v) +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, loc=loc) } // Encodes the CBOR value into binary CBOR written to the given encoder. // 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 if e.temp_allocator.procedure == nil { @@ -366,21 +367,21 @@ _encode_u64_exact :: proc(w: io.Writer, v: u64, major: Major = .Unsigned) -> (er return } -_decode_bytes_ptr :: proc(d: Decoder, add: Add, type: Major = .Bytes) -> (v: ^Bytes, err: Decode_Error) { - v = new(Bytes) or_return - defer if err != nil { free(v) } +_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, allocator, loc) or_return + 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 } -_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 add := add 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) } buf_stream := strings.to_stream(&buf) @@ -426,40 +427,40 @@ _encode_bytes :: proc(e: Encoder, val: Bytes, major: Major = .Bytes) -> (err: En return } -_decode_text_ptr :: proc(d: Decoder, add: Add) -> (v: ^Text, err: Decode_Error) { - v = new(Text) or_return +_decode_text_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: ^Text, err: Decode_Error) { + v = new(Text, allocator, loc) or_return defer if err != nil { free(v) } - v^ = _decode_text(d, add) or_return + v^ = _decode_text(d, add, allocator, loc) or_return return } -_decode_text :: proc(d: Decoder, add: Add, allocator := context.allocator) -> (v: Text, err: Decode_Error) { - return (Text)(_decode_bytes(d, add, .Text, allocator) or_return), nil +_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, loc) or_return), nil } _encode_text :: proc(e: Encoder, val: Text) -> Encode_Error { return _encode_bytes(e, transmute([]byte)val, .Text) } -_decode_array_ptr :: proc(d: Decoder, add: Add) -> (v: ^Array, err: Decode_Error) { - v = new(Array) or_return +_decode_array_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: ^Array, err: Decode_Error) { + v = new(Array, allocator, loc) or_return defer if err != nil { free(v) } - v^ = _decode_array(d, add) or_return + v^ = _decode_array(d, add, allocator, loc) or_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 - array := make([dynamic]Value, 0, scap) or_return + array := make([dynamic]Value, 0, scap, allocator, loc) or_return defer if err != nil { - for entry in array { destroy(entry) } - delete(array) + for entry in array { destroy(entry, allocator) } + delete(array, loc) } 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 { break } else if verr != nil { @@ -485,39 +486,39 @@ _encode_array :: proc(e: Encoder, arr: Array) -> Encode_Error { return nil } -_decode_map_ptr :: proc(d: Decoder, add: Add) -> (v: ^Map, err: Decode_Error) { - v = new(Map) or_return +_decode_map_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: ^Map, err: Decode_Error) { + v = new(Map, allocator, loc) or_return defer if err != nil { free(v) } - v^ = _decode_map(d, add) or_return + v^ = _decode_map(d, add, allocator, loc) or_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 - items := make([dynamic]Map_Entry, 0, scap) or_return + items := make([dynamic]Map_Entry, 0, scap, allocator, loc) or_return defer if err != nil { for entry in items { destroy(entry.key) destroy(entry.value) } - delete(items) + delete(items, loc) } 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 { break } else if kerr != nil { return nil, kerr } - value := _decode_from_decoder(d) or_return + value := _decode_from_decoder(d, {}, allocator, loc) or_return append(&items, Map_Entry{ key = key, value = value, - }) or_return + }, loc) or_return } if .Shrink_Excess in d.flags { shrink(&items) } @@ -578,20 +579,20 @@ _encode_map :: proc(e: Encoder, m: Map) -> (err: Encode_Error) { return nil } -_decode_tag_ptr :: proc(d: Decoder, add: Add) -> (v: Value, err: Decode_Error) { - tag := _decode_tag(d, add) or_return +_decode_tag_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) { + tag := _decode_tag(d, add, allocator, loc) or_return if t, ok := tag.?; ok { defer if err != nil { destroy(t.value) } - tp := new(Tag) or_return + tp := new(Tag, allocator, loc) or_return tp^ = t return tp, nil } // 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 // 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{ 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 { @@ -883,4 +884,4 @@ _encode_deterministic_f64 :: proc(w: io.Writer, v: f64) -> io.Error { } return _encode_f64_exact(w, v) -} +} \ No newline at end of file diff --git a/core/encoding/cbor/marshal.odin b/core/encoding/cbor/marshal.odin index 37c9dd18014..775eafd9ce9 100644 --- a/core/encoding/cbor/marshal.odin +++ b/core/encoding/cbor/marshal.odin @@ -45,8 +45,8 @@ marshal :: marshal_into // 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. -marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (bytes: []byte, err: Marshal_Error) { - b, alloc_err := strings.builder_make(allocator) +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, loc=loc) // The builder as a stream also returns .EOF if it ran out of memory so this is consistent. if alloc_err != nil { 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) } - 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 } @@ -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. // 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 { - return marshal_into_writer(strings.to_writer(b), v, flags, temp_allocator) +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, loc=loc) } // 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. -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} - 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. // 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 if e.temp_allocator.procedure == nil { diff --git a/core/encoding/cbor/unmarshal.odin b/core/encoding/cbor/unmarshal.odin index a1524d9f465..c31ba1d92ae 100644 --- a/core/encoding/cbor/unmarshal.odin +++ b/core/encoding/cbor/unmarshal.odin @@ -31,8 +31,8 @@ unmarshal :: proc { 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) { - err = unmarshal_from_decoder(Decoder{ DEFAULT_MAX_PRE_ALLOC, flags, r }, ptr, allocator, temp_allocator) +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, loc) // 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 } @@ -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. -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 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. if err == .EOF { err = .Unexpected_EOF } 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 - 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. 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.temp_allocator = temp_allocator 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} - 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 ti := reflect.type_info_base(type_info_of(v.id)) 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`. switch &dst in v { 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 } @@ -308,7 +308,7 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header) -> (err: Unmarshal_Err if impl, ok := _tag_implementations_nr[nr]; ok { return impl->unmarshal(d, nr, v) } else if nr == TAG_OBJECT_TYPE { - return _unmarshal_union(d, v, ti, hdr) + return _unmarshal_union(d, v, ti, hdr, loc=loc) } else { // Discard the tag info and unmarshal as its value. 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) - case .Bytes: return _unmarshal_bytes(d, v, ti, hdr, add) - case .Text: return _unmarshal_string(d, v, ti, hdr, add) - case .Array: return _unmarshal_array(d, v, ti, hdr, add) - case .Map: return _unmarshal_map(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, allocator=allocator, loc=loc) + case .Array: return _unmarshal_array(d, v, ti, hdr, add, allocator=allocator, loc=loc) + case .Map: return _unmarshal_map(d, v, ti, hdr, add, allocator=allocator, loc=loc) 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 { 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 { 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) } - 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^ = transmute(mem.Raw_Slice)bytes 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) } - 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.data = raw_data(bytes) raw.len = len(bytes) raw.cap = len(bytes) - raw.allocator = context.allocator + raw.allocator = allocator return 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) } -_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 { 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 { 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. case reflect.Type_Info_Enum: - text := err_conv(_decode_text(d, add, allocator=context.temp_allocator)) or_return - defer delete(text, context.temp_allocator) + text := err_conv(_decode_text(d, add, allocator=temp_allocator, loc=loc)) or_return + defer delete(text, temp_allocator, loc) for name, i in t.names { 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: - text := err_conv(_decode_text(d, add, allocator=context.temp_allocator)) or_return - defer delete(text, context.temp_allocator) + text := err_conv(_decode_text(d, add, allocator=temp_allocator, loc=loc)) or_return + defer delete(text, temp_allocator, loc) r := (^rune)(v.data) 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) } -_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( d: Decoder, da: ^mem.Raw_Dynamic_Array, elemt: ^reflect.Type_Info, length: int, growable := true, + allocator := context.allocator, + loc := #caller_location, ) -> (out_of_space: bool, err: Unmarshal_Error) { for idx: uintptr = 0; length == -1 || idx < uintptr(length); idx += 1 { 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 } 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. 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 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. switch &dst in v { 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 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 } @@ -480,8 +482,8 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header case reflect.Type_Info_Slice: 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 - defer if err != nil { mem.free_bytes(data) } + 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, allocator=allocator, loc=loc) } 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 { // 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) @@ -500,8 +502,8 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header case reflect.Type_Info_Dynamic_Array: 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 - defer if err != nil { mem.free_bytes(data) } + 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, allocator=allocator, loc=loc) } raw := (^mem.Raw_Dynamic_Array)(v.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 { // 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 @@ -525,7 +527,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header 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 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) } - 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 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) } - 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 switch ti.id { @@ -575,7 +577,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header 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 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 - 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_maj, entry_add := _header_split(entry_hdr) #partial switch entry_maj { 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 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) return case: @@ -615,10 +617,10 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, // Allow generically storing the map array. switch &dst in v { 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 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 } @@ -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 // contains an array of exactly two elements, the first is a textual representation of the following // 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 #partial switch t in ti.variant { 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: if vti.name == target_name { 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: @@ -804,7 +806,7 @@ _unmarshal_union :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header if variant_name == target_name { 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) } } } diff --git a/core/encoding/hex/hex.odin b/core/encoding/hex/hex.odin index dbffe216b7f..c2cd89c5b3a 100644 --- a/core/encoding/hex/hex.odin +++ b/core/encoding/hex/hex.odin @@ -2,8 +2,8 @@ package encoding_hex import "core:strings" -encode :: proc(src: []byte, allocator := context.allocator) -> []byte #no_bounds_check { - dst := make([]byte, len(src) * 2, allocator) +encode :: proc(src: []byte, allocator := context.allocator, loc := #caller_location) -> []byte #no_bounds_check { + dst := make([]byte, len(src) * 2, allocator, loc) for i, j := 0, 0; i < len(src); i += 1 { v := src[i] 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 { 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 { p := src[j-1] q := src[j] @@ -69,5 +69,4 @@ hex_digit :: proc(char: byte) -> (u8, bool) { case 'A' ..= 'F': return char - 'A' + 10, true case: return 0, false } -} - +} \ No newline at end of file diff --git a/core/encoding/hxa/hxa.odin b/core/encoding/hxa/hxa.odin index 9b24ede9cae..9d0c5819601 100644 --- a/core/encoding/hxa/hxa.odin +++ b/core/encoding/hxa/hxa.odin @@ -160,34 +160,35 @@ CONVENTION_SOFT_TRANSFORM :: "transform" /* 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 { 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 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 { case Node_Geometry: - delete(n.corner_stack, allocator) - delete(n.edge_stack, allocator) - delete(n.face_stack, allocator) + delete(n.corner_stack, allocator, loc=loc) + delete(n.vertex_stack, allocator, loc=loc) + delete(n.edge_stack, allocator, loc=loc) + delete(n.face_stack, allocator, loc=loc) 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) { - nodes_destroy(file.nodes, file.allocator) - delete(file.backing, file.allocator) -} +file_destroy :: proc(file: File, loc := #caller_location) { + nodes_destroy(file.nodes, file.allocator, loc=loc) + delete(file.backing, file.allocator, loc=loc) +} \ No newline at end of file diff --git a/core/encoding/hxa/read.odin b/core/encoding/hxa/read.odin index f37dc3193f8..5c850322988 100644 --- a/core/encoding/hxa/read.odin +++ b/core/encoding/hxa/read.odin @@ -11,24 +11,21 @@ Read_Error :: enum { 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 - data, ok := os.read_entire_file(filename) + data, ok := os.read_entire_file(filename, allocator, loc) if !ok { err = .Unable_To_Read_File + delete(data, allocator, loc) return } - defer if !ok { - delete(data) - } else { - file.backing = data - } - file, err = read(data, filename, print_error, allocator) + file, err = read(data, filename, print_error, allocator, loc) + file.backing = data return } -read :: proc(data: []byte, filename := "", print_error := false, allocator := context.allocator) -> (file: File, err: Read_Error) { +read :: proc(data: []byte, filename := "", print_error := false, allocator := context.allocator, loc := #caller_location) -> (file: File, err: Read_Error) { Reader :: struct { filename: string, data: []byte, @@ -79,8 +76,8 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato return string(data[:len]), nil } - read_meta :: proc(r: ^Reader, capacity: u32le) -> (meta_data: []Meta, err: Read_Error) { - meta_data = make([]Meta, int(capacity)) + 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), allocator=allocator) count := 0 defer meta_data = meta_data[:count] for &m in meta_data { @@ -111,10 +108,10 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato 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 layer_count := 0 - layers = make(Layer_Stack, stack_count) + layers = make(Layer_Stack, stack_count, allocator=allocator, loc=loc) defer layers = layers[:layer_count] for &layer in layers { layer.name = read_name(r) or_return @@ -170,7 +167,8 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato node_count := 0 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 { nodes_destroy(file.nodes) file.nodes = nil @@ -198,15 +196,15 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato case .Geometry: g: Node_Geometry - g.vertex_count = read_value(r, u32le) or_return - g.vertex_stack = read_layer_stack(r, g.vertex_count) 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.vertex_count = read_value(r, u32le) 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.corner_stack = read_layer_stack(r, g.edge_corner_count, loc=loc) or_return 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_stack = read_layer_stack(r, g.face_count) or_return + g.face_count = read_value(r, u32le) or_return + g.face_stack = read_layer_stack(r, g.face_count, loc=loc) or_return node.content = g @@ -233,4 +231,4 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato } return -} +} \ No newline at end of file diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index 4f5b50ec549..2933adf9ae4 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -62,8 +62,8 @@ Marshal_Options :: struct { mjson_skipped_first_braces_end: bool, } -marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) { - b := strings.builder_make(allocator) +marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Marshal_Error) { + b := strings.builder_make(allocator, loc) defer if err != nil { strings.builder_destroy(&b) } diff --git a/core/encoding/json/parser.odin b/core/encoding/json/parser.odin index 3973725dc6b..38f71edf6c9 100644 --- a/core/encoding/json/parser.odin +++ b/core/encoding/json/parser.odin @@ -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) { - return parse_string(string(data), spec, parse_integers, allocator) +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, 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 p := make_parser_from_string(data, spec, parse_integers, allocator) switch p.spec { case .JSON: - return parse_object(&p) + return parse_object(&p, loc) case .JSON5: - return parse_value(&p) + return parse_value(&p, loc) case .SJSON: #partial switch p.curr_token.kind { 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 { @@ -106,7 +106,7 @@ parse_comma :: proc(p: ^Parser) -> (do_break: bool) { 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 token := p.curr_token #partial switch token.kind { @@ -142,13 +142,13 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) { case .String: advance_token(p) - return unquote_string(token, p.spec, p.allocator) + return unquote_string(token, p.spec, p.allocator, loc) case .Open_Brace: - return parse_object(p) + return parse_object(p, loc) case .Open_Bracket: - return parse_array(p) + return parse_array(p, loc) case: if p.spec != .JSON { @@ -176,7 +176,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) { return } -parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) { +parse_array :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) { err = .None expect_token(p, .Open_Bracket) or_return @@ -184,14 +184,14 @@ parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) { array.allocator = p.allocator defer if err != nil { for elem in array { - destroy_value(elem) + destroy_value(elem, loc=loc) } - delete(array) + delete(array, loc) } for p.curr_token.kind != .Close_Bracket { - elem := parse_value(p) or_return - append(&array, elem) + elem := parse_value(p, loc) or_return + append(&array, elem, loc) if parse_comma(p) { break @@ -228,38 +228,39 @@ clone_string :: proc(s: string, allocator: mem.Allocator, loc := #caller_locatio 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 if p.spec != .JSON { 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 { err = .Expected_String_For_Object_Key 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) { - obj.allocator = p.allocator +parse_object_body :: proc(p: ^Parser, end_token: Token_Kind, loc := #caller_location) -> (obj: Object, err: Error) { + obj = make(Object, allocator=p.allocator, loc=loc) + defer if err != nil { for key, elem in obj { - delete(key, p.allocator) - destroy_value(elem) + delete(key, p.allocator, loc) + destroy_value(elem, loc=loc) } - delete(obj) + delete(obj, loc) } 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 - elem := parse_value(p) or_return + elem := parse_value(p, loc) or_return if key in obj { err = .Duplicate_Object_Key - delete(key, p.allocator) + delete(key, p.allocator, loc) 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 // want to allocate anything 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 { 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 } -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 - 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 return obj, .None } @@ -480,4 +481,4 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a } return string(b[:w]), nil -} +} \ No newline at end of file diff --git a/core/encoding/json/types.odin b/core/encoding/json/types.odin index 73e1836156b..41eb2137714 100644 --- a/core/encoding/json/types.odin +++ b/core/encoding/json/types.odin @@ -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 #partial switch v in value { case Object: for key, elem in v { - delete(key) - destroy_value(elem) + delete(key, loc=loc) + destroy_value(elem, loc=loc) } - delete(v) + delete(v, loc=loc) case Array: for elem in v { - destroy_value(elem) + destroy_value(elem, loc=loc) } - delete(v) + delete(v, loc=loc) case String: - delete(v) + delete(v, loc=loc) } } diff --git a/core/log/file_console_logger.odin b/core/log/file_console_logger.odin index bcce6757887..fb968ccb626 100644 --- a/core/log/file_console_logger.odin +++ b/core/log/file_console_logger.odin @@ -1,6 +1,7 @@ //+build !freestanding package log +import "core:encoding/ansi" import "core:fmt" import "core:strings" 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. buf := strings.builder_from_bytes(backing[:]) - do_level_header(options, level, &buf) + do_level_header(options, &buf, level) when time.IS_SUPPORTED { - if Full_Timestamp_Opts & options != nil { - 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_time_header(options, &buf, time.now()) } 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) } -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" - RED :: "\x1b[31m" - YELLOW :: "\x1b[33m" - DARK_GREY :: "\x1b[90m" + RESET :: ansi.CSI + ansi.RESET + ansi.SGR + RED :: ansi.CSI + ansi.FG_RED + ansi.SGR + YELLOW :: ansi.CSI + ansi.FG_YELLOW + ansi.SGR + DARK_GREY :: ansi.CSI + ansi.FG_BRIGHT_BLACK + ansi.SGR col := RESET 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) { if Location_Header_Opts & opts == nil { return diff --git a/core/mem/rollback_stack_allocator.odin b/core/mem/rollback_stack_allocator.odin new file mode 100644 index 00000000000..f5e428d878f --- /dev/null +++ b/core/mem/rollback_stack_allocator.odin @@ -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 +} diff --git a/core/mem/tracking_allocator.odin b/core/mem/tracking_allocator.odin index bc624617d23..1b57e5fb486 100644 --- a/core/mem/tracking_allocator.odin +++ b/core/mem/tracking_allocator.odin @@ -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) { sync.mutex_lock(&t.mutex) clear(&t.allocation_map) @@ -55,6 +56,19 @@ tracking_allocator_clear :: proc(t: ^Tracking_Allocator) { 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) tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator { diff --git a/core/strings/builder.odin b/core/strings/builder.odin index 72eb815f9f9..11885b68966 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -350,9 +350,9 @@ Output: 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) - append(&b.buf, x) + append(&b.buf, x, loc) n1 := len(b.buf) return n1-n0 } @@ -380,9 +380,9 @@ NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` Returns: - 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) - append(&b.buf, ..x) + append(&b.buf, ..x, loc=loc) n1 := len(b.buf) return n1-n0 } diff --git a/core/sys/unix/pthread_freebsd.odin b/core/sys/unix/pthread_freebsd.odin index 3417d3943bd..5f4dac28942 100644 --- a/core/sys/unix/pthread_freebsd.odin +++ b/core/sys/unix/pthread_freebsd.odin @@ -95,7 +95,7 @@ sem_t :: struct { PTHREAD_CANCEL_ENABLE :: 0 PTHREAD_CANCEL_DISABLE :: 1 PTHREAD_CANCEL_DEFERRED :: 0 -PTHREAD_CANCEL_ASYNCHRONOUS :: 1 +PTHREAD_CANCEL_ASYNCHRONOUS :: 2 foreign import "system:pthread" @@ -119,4 +119,4 @@ foreign pthread { 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_cancel :: proc (thread: pthread_t) -> c.int --- -} \ No newline at end of file +} diff --git a/core/sys/unix/pthread_openbsd.odin b/core/sys/unix/pthread_openbsd.odin index 7ae82e66254..855e7d99c72 100644 --- a/core/sys/unix/pthread_openbsd.odin +++ b/core/sys/unix/pthread_openbsd.odin @@ -49,7 +49,7 @@ sem_t :: distinct rawptr PTHREAD_CANCEL_ENABLE :: 0 PTHREAD_CANCEL_DISABLE :: 1 PTHREAD_CANCEL_DEFERRED :: 0 -PTHREAD_CANCEL_ASYNCHRONOUS :: 1 +PTHREAD_CANCEL_ASYNCHRONOUS :: 2 foreign import libc "system:c" @@ -71,4 +71,4 @@ foreign libc { 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_cancel :: proc (thread: pthread_t) -> c.int --- -} \ No newline at end of file +} diff --git a/core/sys/unix/pthread_unix.odin b/core/sys/unix/pthread_unix.odin index 5760560eec4..c876a214a8a 100644 --- a/core/sys/unix/pthread_unix.odin +++ b/core/sys/unix/pthread_unix.odin @@ -116,4 +116,5 @@ foreign pthread { 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_testcancel :: proc () --- } diff --git a/core/testing/events.odin b/core/testing/events.odin new file mode 100644 index 00000000000..bab35aaad8e --- /dev/null +++ b/core/testing/events.odin @@ -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, +} diff --git a/core/testing/logging.odin b/core/testing/logging.odin new file mode 100644 index 00000000000..5bbbffeae00 --- /dev/null +++ b/core/testing/logging.odin @@ -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) +} diff --git a/core/testing/reporting.odin b/core/testing/reporting.odin new file mode 100644 index 00000000000..92e144cccd6 --- /dev/null +++ b/core/testing/reporting.odin @@ -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) + } +} diff --git a/core/testing/runner.odin b/core/testing/runner.odin index 0039f1939d3..c82aa1fda43 100644 --- a/core/testing/runner.odin +++ b/core/testing/runner.odin @@ -1,73 +1,820 @@ //+private 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" +@require import pkg_log "core:log" +import "core:mem" import "core:os" import "core:slice" +@require import "core:strings" +import "core:sync/chan" +import "core:thread" +import "core:time" -reset_t :: proc(t: ^T) { - clear(&t.cleanups) - t.error_count = 0 +// Specify how many threads to use when running tests. +TEST_THREADS : int : #config(ODIN_TEST_THREADS, 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) { 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) } + + 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 { - stream := os.stream_from_handle(os.stdout) - w := io.to_writer(stream) + BATCH_BUFFER_SIZE :: 32 * mem.Kilobyte + POOL_BLOCK_SIZE :: 16 * mem.Kilobyte + CLIPBOARD_BUFFER_SIZE :: 16 * mem.Kilobyte + + BUFFERED_EVENTS_PER_CHANNEL :: 16 + RESERVED_LOG_MESSAGES :: 64 + RESERVED_TEST_FAILURES :: 64 + + ERROR_STRING_TIMEOUT : string : "Test timed out." + 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 + + safe_delete_string :: proc(s: string, allocator := context.allocator) { + // Guard against bad frees on static strings. + switch raw_data(s) { + case raw_data(ERROR_STRING_TIMEOUT), raw_data(ERROR_STRING_UNKNOWN): + return + case: + delete(s, allocator) + } + } + + 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) + } + } + } - t := &T{} - t.w = w - reserve(&t.cleanups, 1024) - defer delete(t.cleanups) + // Intentional shadow with user-specified tests. + internal_tests := select_internal_tests[:] + } + total_failure_count := 0 total_success_count := 0 - total_test_count := len(internal_tests) + 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 { + // NOTE(Feoramund): The old test runner skipped over tests with nil + // procedures, but I couldn't find any case where they occurred. + // This assert stands to prevent any oversight on my part. + fmt.assertf(it.p != nil, "Test %s.%s has procedure.", it.pkg, it.name) + } slice.sort_by(internal_tests, proc(a, b: Internal_Test) -> bool { - if a.pkg < b.pkg { - return true + if a.pkg == b.pkg { + return a.name < b.name + } else { + return a.pkg < b.pkg } - return a.name < b.name }) - prev_pkg := "" + // -- Set thread count. - for it in internal_tests { - if it.p == nil { - total_test_count -= 1 - continue + 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) - free_all(context.temp_allocator) - reset_t(t) - defer end_t(t) + 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])) + } + } - if prev_pkg != it.pkg { - prev_pkg = it.pkg - logf(t, "[Package: %s]", it.pkg) + defer #no_bounds_check for i in 0 ..< thread_count { + when TRACKING_MEMORY { + mem.tracking_allocator_destroy(&task_memory_trackers[i]) } + mem.rollback_stack_destroy(&task_allocators[i]) + } - logf(t, "[Test: %s]", it.name) + 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) - run_internal_test(t, it) + 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) - if failed(t) { - logf(t, "[%s : FAILURE]", it.name) - } else { - logf(t, "[%s : SUCCESS]", it.name) - total_success_count += 1 + 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 } } - logf(t, "----------------------------------------") - if total_test_count == 0 { - log(t, "NO TESTS RAN") + + // -- 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 { - logf(t, "%d/%d SUCCESSFUL", total_success_count, total_test_count) + 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 } diff --git a/core/testing/runner_other.odin b/core/testing/runner_other.odin deleted file mode 100644 index f3271d2090a..00000000000 --- a/core/testing/runner_other.odin +++ /dev/null @@ -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) { - -} \ No newline at end of file diff --git a/core/testing/runner_windows.odin b/core/testing/runner_windows.odin deleted file mode 100644 index 15264355bc9..00000000000 --- a/core/testing/runner_windows.odin +++ /dev/null @@ -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 -} diff --git a/core/testing/signal_handler.odin b/core/testing/signal_handler.odin new file mode 100644 index 00000000000..891f6bbb638 --- /dev/null +++ b/core/testing/signal_handler.odin @@ -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() +} diff --git a/core/testing/signal_handler_libc.odin b/core/testing/signal_handler_libc.odin new file mode 100644 index 00000000000..d76fdd66bb3 --- /dev/null +++ b/core/testing/signal_handler_libc.odin @@ -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 +} diff --git a/core/testing/signal_handler_other.odin b/core/testing/signal_handler_other.odin new file mode 100644 index 00000000000..04981f5afcf --- /dev/null +++ b/core/testing/signal_handler_other.odin @@ -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 +} diff --git a/core/testing/testing.odin b/core/testing/testing.odin index a8c5ffa48f6..92b4d391df8 100644 --- a/core/testing/testing.odin +++ b/core/testing/testing.odin @@ -1,10 +1,11 @@ package testing -import "core:fmt" -import "core:io" -import "core:time" import "base:intrinsics" +import "base:runtime" +import pkg_log "core:log" import "core:reflect" +import "core:sync/chan" +import "core:time" _ :: reflect // alias reflect to nothing to force visibility for -vet @@ -22,44 +23,55 @@ Internal_Test :: struct { Internal_Cleanup :: struct { procedure: proc(rawptr), user_data: rawptr, + ctx: runtime.Context, } T :: struct { 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, + // 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() -> !, } +@(deprecated="prefer `log.error`") error :: proc(t: ^T, args: ..any, loc := #caller_location) { - fmt.wprintf(t.w, "%v: ", loc) - fmt.wprintln(t.w, ..args) - t.error_count += 1 + pkg_log.error(..args, location = loc) } +@(deprecated="prefer `log.errorf`") errorf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) { - fmt.wprintf(t.w, "%v: ", loc) - fmt.wprintf(t.w, format, ..args) - fmt.wprintln(t.w) - t.error_count += 1 + pkg_log.errorf(format, ..args, location = loc) } fail :: proc(t: ^T, loc := #caller_location) { - error(t, "FAIL", loc=loc) - t.error_count += 1 + pkg_log.error("FAIL", location=loc) } fail_now :: proc(t: ^T, msg := "", loc := #caller_location) { if msg != "" { - error(t, "FAIL:", msg, loc=loc) + pkg_log.error("FAIL:", msg, location=loc) } else { - error(t, "FAIL", loc=loc) + pkg_log.error("FAIL", location=loc) } - t.error_count += 1 if t._fail_now != nil { t._fail_now() } @@ -69,32 +81,34 @@ failed :: proc(t: ^T) -> bool { return t.error_count != 0 } +@(deprecated="prefer `log.info`") 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) { - fmt.wprintf(t.w, format, ..args) - fmt.wprintln(t.w) + pkg_log.infof(format, ..args, location = loc) } -// 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 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. +// Each procedure will use a copy of the context at the time of registering. 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 { if !ok { - error(t, msg, loc=loc) + pkg_log.error(msg, location=loc) } return ok } expectf :: proc(t: ^T, ok: bool, format: string, args: ..any, loc := #caller_location) -> bool { if !ok { - errorf(t, format, ..args, loc=loc) + pkg_log.errorf(format, ..args, location=loc) } 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) { ok := value == expected || reflect.is_nil(value) && reflect.is_nil(expected) 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 } 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, + }) } diff --git a/core/text/i18n/qt_linguist.odin b/core/text/i18n/qt_linguist.odin index 0e75df8738d..bdd3f5fd70f 100644 --- a/core/text/i18n/qt_linguist.odin +++ b/core/text/i18n/qt_linguist.odin @@ -162,8 +162,6 @@ parse_qt_linguist_file :: proc(filename: string, options := DEFAULT_PARSE_OPTION context.allocator = allocator data, data_ok := os.read_entire_file(filename) - defer delete(data) - if !data_ok { return {}, .File_Error } return parse_qt_linguist_from_bytes(data, options, pluralizer, allocator) diff --git a/core/thread/thread_pool.odin b/core/thread/thread_pool.odin index fddcac89e60..da5e116ff9d 100644 --- a/core/thread/thread_pool.odin +++ b/core/thread/thread_pool.odin @@ -44,6 +44,29 @@ Pool :: struct { 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 // it is destroyed. // @@ -58,21 +81,11 @@ pool_init :: proc(pool: ^Pool, allocator: mem.Allocator, thread_count: int) { pool.is_running = true for _, i in pool.threads { - t := create(proc(t: ^Thread) { - pool := (^Pool)(t.data) - - 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 := create(pool_thread_runner) + data := new(Pool_Thread_Data) + data.pool = pool t.user_index = i - t.data = pool + t.data = data pool.threads[i] = t } } @@ -82,6 +95,8 @@ pool_destroy :: proc(pool: ^Pool) { delete(pool.tasks_done) for &t in pool.threads { + data := cast(^Pool_Thread_Data)t.data + free(data, pool.allocator) destroy(t) } @@ -103,7 +118,7 @@ pool_join :: proc(pool: ^Pool) { yield() -started_count: int + started_count: int for started_count < len(pool.threads) { started_count = 0 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) } +// 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 // debugging. Don't rely on this value being consistent with other num_* // values. diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 376e56a8ed5..7bdec376bc7 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -893,7 +893,6 @@ struct BuildContext { u32 cmd_doc_flags; Array extra_packages; - StringSet test_names; bool test_all_packages; gbAffinity affinity; diff --git a/src/checker.cpp b/src/checker.cpp index 2fd274975ef..97e685d3358 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -5852,35 +5852,6 @@ gb_internal void remove_neighbouring_duplicate_entires_from_sorted_array(Arrayinfo.testing_procedures, init_procedures_cmp); 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; - } - } - } diff --git a/src/main.cpp b/src/main.cpp index 80fc995cf6e..d7d2e3f4a6b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -276,8 +276,6 @@ enum BuildFlagKind { BuildFlag_RelocMode, BuildFlag_DisableRedZone, - BuildFlag_TestName, - BuildFlag_DisallowDo, BuildFlag_DefaultToNilAllocator, BuildFlag_DefaultToPanicAllocator, @@ -471,8 +469,6 @@ gb_internal bool parse_build_flags(Array args) { 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_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_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); @@ -1119,21 +1115,6 @@ gb_internal bool parse_build_flags(Array args) { case BuildFlag_DisableRedZone: build_context.disable_red_zone = true; 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: build_context.disallow_do = true; break; @@ -1962,10 +1943,6 @@ gb_internal void print_show_help(String const arg0, String const &command) { } if (test_only) { - print_usage_line(1, "-test-name:"); - print_usage_line(2, "Runs specific test only by name."); - print_usage_line(0, ""); - print_usage_line(1, "-all-packages"); print_usage_line(2, "Tests all packages imported into the given initial package."); print_usage_line(0, ""); @@ -2489,7 +2466,6 @@ int main(int arg_count, char const **arg_ptr) { TIME_SECTION("init args"); map_init(&build_context.defined_values); build_context.extra_packages.allocator = heap_allocator(); - string_set_init(&build_context.test_names); Array args = setup_args(arg_count, arg_ptr); diff --git a/tests/benchmark/Makefile b/tests/benchmark/Makefile new file mode 100644 index 00000000000..840174dbb17 --- /dev/null +++ b/tests/benchmark/Makefile @@ -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_* \ No newline at end of file diff --git a/tests/benchmark/build.bat b/tests/benchmark/build.bat new file mode 100644 index 00000000000..30b03006689 --- /dev/null +++ b/tests/benchmark/build.bat @@ -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 diff --git a/tests/benchmark/crypto/benchmark_crypto.odin b/tests/benchmark/crypto/benchmark_crypto.odin new file mode 100644 index 00000000000..e90216ad666 --- /dev/null +++ b/tests/benchmark/crypto/benchmark_crypto.odin @@ -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, + ) +} diff --git a/tests/benchmark/hash/benchmark_hash.odin b/tests/benchmark/hash/benchmark_hash.odin new file mode 100644 index 00000000000..84eb827e77e --- /dev/null +++ b/tests/benchmark/hash/benchmark_hash.odin @@ -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, + ) +} \ No newline at end of file diff --git a/tests/common/common.odin b/tests/common/common.odin deleted file mode 100644 index 021fb21c5d1..00000000000 --- a/tests/common/common.odin +++ /dev/null @@ -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 -} diff --git a/tests/core/Makefile b/tests/core/Makefile index 9026ed3d9b8..85f3783b452 100644 --- a/tests/core/Makefile +++ b/tests/core/Makefile @@ -1,16 +1,15 @@ ODIN=../../odin PYTHON=$(shell which python3) -COMMON=-vet -strict-style -COLLECTION=-collection:tests=.. +COMMON=-no-bounds-check -vet -strict-style -define:ODIN_TEST_FANCY=false all: all_bsd \ net_test -all_bsd: c_libc_test \ +all_bsd: download_test_assets \ + c_libc_test \ compress_test \ container_test \ crypto_test \ - download_test_assets \ encoding_test \ filepath_test \ fmt_test \ @@ -21,86 +20,92 @@ all_bsd: c_libc_test \ match_test \ math_test \ noise_test \ + odin_test \ os_exit_test \ reflect_test \ + runtime_test \ slice_test \ strings_test \ thread_test \ - runtime_test \ - time_test \ - fmt_test + time_test download_test_assets: $(PYTHON) download_assets.py -image_test: - $(ODIN) run image $(COMMON) -out:test_core_image +c_libc_test: + $(ODIN) test c/libc $(COMMON) -out:test_core_libc compress_test: - $(ODIN) run compress $(COMMON) -out:test_core_compress + $(ODIN) test compress $(COMMON) -out:test_core_compress 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 +crypto_test: + $(ODIN) test crypto $(COMMON) -o:speed -out:test_crypto + +encoding_test: + $(ODIN) test encoding/base64 $(COMMON) -out:test_base64 + $(ODIN) test encoding/cbor $(COMMON) -out:test_cbor + $(ODIN) test encoding/hex $(COMMON) -out:test_hex + $(ODIN) test encoding/hxa $(COMMON) -out:test_hxa + $(ODIN) test encoding/json $(COMMON) -out:test_json + $(ODIN) test encoding/varint $(COMMON) -out:test_varint + $(ODIN) test encoding/xml $(COMMON) -out:test_xml + +filepath_test: + $(ODIN) test path/filepath $(COMMON) -out:test_core_filepath + +fmt_test: + $(ODIN) test fmt $(COMMON) -out:test_core_fmt hash_test: - $(ODIN) run hash $(COMMON) -o:speed -no-bounds-check -out:test_hash + $(ODIN) test hash $(COMMON) -o:speed -out:test_hash -crypto_test: - $(ODIN) run crypto $(COMMON) $(COLLECTION) -o:speed -no-bounds-check -out:test_crypto +image_test: + $(ODIN) test image $(COMMON) -out:test_core_image -noise_test: - $(ODIN) run math/noise $(COMMON) -out:test_noise +i18n_test: + $(ODIN) test text/i18n $(COMMON) -out:test_core_i18n -encoding_test: - $(ODIN) run encoding/hxa $(COMMON) $(COLLECTION) -out:test_hxa - $(ODIN) run encoding/json $(COMMON) -out:test_json - $(ODIN) run encoding/varint $(COMMON) -out:test_varint - $(ODIN) run encoding/xml $(COMMON) -out:test_xml - $(ODIN) run encoding/cbor $(COMMON) -out:test_cbor - $(ODIN) run encoding/hex $(COMMON) -out:test_hex - $(ODIN) run encoding/base64 $(COMMON) -out:test_base64 +match_test: + $(ODIN) test text/match $(COMMON) -out:test_core_match math_test: - $(ODIN) run math $(COMMON) $(COLLECTION) -out:test_core_math + $(ODIN) test math $(COMMON) -out:test_core_math linalg_glsl_math_test: - $(ODIN) run math/linalg/glsl $(COMMON) $(COLLECTION) -out:test_linalg_glsl_math + $(ODIN) test math/linalg/glsl $(COMMON) -out:test_linalg_glsl_math -filepath_test: - $(ODIN) run path/filepath $(COMMON) $(COLLECTION) -out:test_core_filepath - -reflect_test: - $(ODIN) run reflect $(COMMON) $(COLLECTION) -out:test_core_reflect +noise_test: + $(ODIN) test math/noise $(COMMON) -out:test_noise -slice_test: - $(ODIN) run slice $(COMMON) -out:test_core_slice +net_test: + $(ODIN) test net $(COMMON) -out:test_core_net os_exit_test: $(ODIN) run os/test_core_os_exit.odin -file -out:test_core_os_exit && exit 1 || exit 0 -i18n_test: - $(ODIN) run text/i18n $(COMMON) -out:test_core_i18n +odin_test: + $(ODIN) test odin $(COMMON) -out:test_core_odin -match_test: - $(ODIN) run text/match $(COMMON) -out:test_core_match +reflect_test: + $(ODIN) test reflect $(COMMON) -out:test_core_reflect -c_libc_test: - $(ODIN) run c/libc $(COMMON) -out:test_core_libc +runtime_test: + $(ODIN) test runtime $(COMMON) -out:test_core_runtime -net_test: - $(ODIN) run net $(COMMON) -out:test_core_net +slice_test: + $(ODIN) test slice $(COMMON) -out:test_core_slice -fmt_test: - $(ODIN) run fmt $(COMMON) -out:test_core_fmt +strings_test: + $(ODIN) test strings $(COMMON) -out:test_core_strings thread_test: - $(ODIN) run thread $(COMMON) -out:test_core_thread - -runtime_test: - $(ODIN) run runtime $(COMMON) -out:test_core_runtime + $(ODIN) test thread $(COMMON) -out:test_core_thread time_test: - $(ODIN) run time $(COMMON) -out:test_core_time + $(ODIN) test time $(COMMON) -out:test_core_time + +clean: + rm test_* \ No newline at end of file diff --git a/tests/core/build.bat b/tests/core/build.bat index 7871e52e2ca..67ac10f86fd 100644 --- a/tests/core/build.bat +++ b/tests/core/build.bat @@ -1,110 +1,119 @@ @echo off -set COMMON=-no-bounds-check -vet -strict-style -set COLLECTION=-collection:tests=.. +set COMMON=-no-bounds-check -vet -strict-style -define:ODIN_TEST_FANCY=false set PATH_TO_ODIN==..\..\odin 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 Running core:compress tests 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 Running core:container tests 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 Running core:crypto tests 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 Running core:encoding tests echo --- -rem %PATH_TO_ODIN% run encoding/hxa %COMMON% %COLLECTION% -out:test_hxa.exe || exit /b -%PATH_TO_ODIN% run encoding/json %COMMON% -out:test_json.exe || exit /b -%PATH_TO_ODIN% run encoding/varint %COMMON% -out:test_varint.exe || exit /b -%PATH_TO_ODIN% run encoding/xml %COMMON% -out:test_xml.exe || exit /b -%PATH_TO_ODIN% test encoding/cbor %COMMON% -out:test_cbor.exe || exit /b -%PATH_TO_ODIN% run encoding/hex %COMMON% -out:test_hex.exe || exit /b -%PATH_TO_ODIN% run encoding/base64 %COMMON% -out:test_base64.exe || exit /b +%PATH_TO_ODIN% test encoding/base64 %COMMON% -out:test_base64.exe || exit /b +%PATH_TO_ODIN% test encoding/cbor %COMMON% -out:test_cbor.exe || exit /b +%PATH_TO_ODIN% test encoding/hex %COMMON% -out:test_hex.exe || exit /b +%PATH_TO_ODIN% test encoding/hxa %COMMON% -out:test_hxa.exe || exit /b +%PATH_TO_ODIN% test encoding/json %COMMON% -out:test_json.exe || exit /b +%PATH_TO_ODIN% test encoding/varint %COMMON% -out:test_varint.exe || exit /b +%PATH_TO_ODIN% test encoding/xml %COMMON% -out:test_xml.exe || exit /b + +echo --- +echo Running core:path/filepath tests +echo --- +%PATH_TO_ODIN% test path/filepath %COMMON% -out:test_core_filepath.exe || exit /b echo --- echo Running core:fmt tests echo --- -%PATH_TO_ODIN% run fmt %COMMON% %COLLECTION% -out:test_core_fmt.exe || exit /b +%PATH_TO_ODIN% test fmt %COMMON% -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 +%PATH_TO_ODIN% test hash %COMMON% -o:speed -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 +%PATH_TO_ODIN% test image %COMMON% -out:test_core_image.exe || exit /b + +echo --- +echo Running core:text/i18n tests +echo --- +%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% run math %COMMON% %COLLECTION% -out:test_core_math.exe || exit /b +%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% run math/linalg/glsl %COMMON% %COLLECTION% -out:test_linalg_glsl.exe || exit /b +%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% run math/noise %COMMON% -out:test_noise.exe || exit /b +%PATH_TO_ODIN% test 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 +%PATH_TO_ODIN% test 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 Running core:path/filepath tests -echo --- -%PATH_TO_ODIN% run path/filepath %COMMON% %COLLECTION% -out:test_core_filepath.exe || exit /b +%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% run reflect %COMMON% %COLLECTION% -out:test_core_reflect.exe || exit /b +%PATH_TO_ODIN% test reflect %COMMON% -out:test_core_reflect.exe || exit /b echo --- echo Running core:runtime tests echo --- -%PATH_TO_ODIN% run runtime %COMMON% %COLLECTION% -out:test_core_runtime.exe || exit /b +%PATH_TO_ODIN% test runtime %COMMON% -out:test_core_runtime.exe || exit /b echo --- echo Running core:slice tests echo --- -%PATH_TO_ODIN% run slice %COMMON% -out:test_core_slice.exe || exit /b +%PATH_TO_ODIN% test slice %COMMON% -out:test_core_slice.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 Running core:text/i18n tests -echo --- -%PATH_TO_ODIN% run text\i18n %COMMON% -out:test_core_i18n.exe || exit /b +%PATH_TO_ODIN% test strings %COMMON% -out:test_core_strings.exe || exit /b echo --- echo Running core:thread tests 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 Running core:time tests echo --- -%PATH_TO_ODIN% run time %COMMON% %COLLECTION% -out:test_core_time.exe || exit /b \ No newline at end of file +%PATH_TO_ODIN% test time %COMMON% -out:test_core_time.exe || exit /b \ No newline at end of file diff --git a/tests/core/c/libc/test_core_libc.odin b/tests/core/c/libc/test_core_libc.odin deleted file mode 100644 index 9b5014dee41..00000000000 --- a/tests/core/c/libc/test_core_libc.odin +++ /dev/null @@ -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) - } -} diff --git a/tests/core/c/libc/test_core_libc_complex_pow.odin b/tests/core/c/libc/test_core_libc_complex_pow.odin index 90928794c45..cd50c8f6a93 100644 --- a/tests/core/c/libc/test_core_libc_complex_pow.odin +++ b/tests/core/c/libc/test_core_libc_complex_pow.odin @@ -1,8 +1,8 @@ package test_core_libc import "core:testing" -import "core:fmt" import "core:c/libc" +import "core:log" reldiff :: proc(lhs, rhs: $T) -> f64 { if lhs == rhs { @@ -14,7 +14,7 @@ reldiff :: proc(lhs, rhs: $T) -> f64 { 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)) if adiff < atol { return true @@ -23,7 +23,7 @@ isclose :: proc(lhs, rhs: $T, rtol:f64 = 1e-12, atol:f64 = 1e-12) -> bool { if rdiff < rtol { 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 } @@ -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 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) { // 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 { complex_power := LIBC_COMPLEX(complex(F(n), F(0.))) 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)) - 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_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) + 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 } } @@ -83,8 +82,8 @@ test_libc_pow_binding :: proc(t: ^testing.T, $LIBC_COMPLEX:typeid, $F:typeid, po expected_real = 0. 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)) - 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_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) + 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 } } diff --git a/tests/core/compress/test_core_compress.odin b/tests/core/compress/test_core_compress.odin index ac7555e9a25..4ab63ae6762 100644 --- a/tests/core/compress/test_core_compress.odin +++ b/tests/core/compress/test_core_compress.odin @@ -15,47 +15,7 @@ import "core:testing" import "core:compress/zlib" import "core:compress/gzip" import "core:compress/shoco" - 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 zlib_test :: proc(t: ^testing.T) { @@ -80,26 +40,14 @@ zlib_test :: proc(t: ^testing.T) { } buf: bytes.Buffer + err := zlib.inflate(ODIN_DEMO, &buf) - track: mem.Tracking_Allocator - 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") + testing.expect(t, err == nil, "ZLIB failed to decompress ODIN_DEMO") 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.") - - expect(t, len(s) == 438, "ZLIB result has an unexpected length.") - + 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.") 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 @@ -117,24 +65,12 @@ gzip_test :: proc(t: ^testing.T) { } buf: bytes.Buffer + err := gzip.load(TEST, &buf) - track: mem.Tracking_Allocator - mem.tracking_allocator_init(&track, context.allocator) - 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'") + testing.expect(t, err == nil, "GZIP failed to decompress TEST") + testing.expect(t, bytes.buffer_to_string(&buf) == "payload", "GZIP result wasn't 'payload'") 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 @@ -168,31 +104,26 @@ shoco_test :: proc(t: ^testing.T) { defer delete(buffer) size, err := shoco.decompress(v.compressed, buffer[:]) - msg := fmt.tprintf("Expected `decompress` to return `nil`, got %v", err) - expect(t, err == nil, msg) + testing.expectf(t, err == nil, "Expected `decompress` to return `nil`, got %v", err) - msg = fmt.tprintf("Decompressed %v bytes into %v. Expected to decompress into %v bytes.", len(v.compressed), size, expected_raw) - expect(t, size == expected_raw, msg) - expect(t, string(buffer[:size]) == string(v.raw), "Decompressed contents don't match.") + testing.expectf(t, size == expected_raw, "Decompressed %v bytes into %v. Expected to decompress into %v bytes", len(v.compressed), size, expected_raw) + testing.expect(t, string(buffer[:size]) == string(v.raw), "Decompressed contents don't match") 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) - expect(t, size == expected_compressed, msg) + testing.expectf(t, size == expected_compressed, "Compressed %v bytes into %v. Expected to compress into %v bytes", expected_raw, size, expected_compressed) 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) - expect(t, err == .Output_Too_Short, msg) + testing.expectf(t, err == .Output_Too_Short, "Decompressing into too small a buffer returned %v, expected `.Output_Too_Short`", err) 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) - expect(t, err == .Output_Too_Short, msg) + testing.expectf(t, err == .Output_Too_Short, "Compressing into too small a buffer returned %v, expected `.Output_Too_Short`", err) 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[:]) - 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) } -} \ No newline at end of file +} diff --git a/tests/core/container/test_core_avl.odin b/tests/core/container/test_core_avl.odin index 2244ab7f6fa..99dbba8b2be 100644 --- a/tests/core/container/test_core_avl.odin +++ b/tests/core/container/test_core_avl.odin @@ -4,53 +4,54 @@ import "core:container/avl" import "core:math/rand" import "core:slice" import "core:testing" -import "core:fmt" -import tc "tests:common" +import "core:log" @(test) 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. tree: avl.Tree(int) avl.init(&tree, slice.cmp_proc(int)) - tc.expect(t, avl.len(&tree) == 0, "empty: len should be 0") - tc.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.len(&tree) == 0, "empty: len should be 0") + testing.expect(t, avl.first(&tree) == nil, "empty: first should be nil") + testing.expect(t, avl.last(&tree) == nil, "empty: last should be nil") 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 - rand.init(&r, random_seed) + rand.init(&r, t.seed) // Test insertion. NR_INSERTS :: 32 + 1 // Ensure at least 1 collision. inserted_map := make(map[int]^avl.Node(int)) + defer delete(inserted_map) for i := 0; i < NR_INSERTS; i += 1 { v := int(rand.uint32(&r) & 0x1f) existing_node, in_map := inserted_map[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 { inserted_map[v] = n } 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) - tc.expect(t, avl.len(&tree) == nrEntries, "insert: len after") + testing.expect(t, avl.len(&tree) == nrEntries, "insert: len after") validate_avl(t, &tree) // Ensure that all entries can be found. for k, v in inserted_map { - tc.expect(t, v == avl.find(&tree, k), "Find(): Node") - tc.expect(t, k == v.value, "Find(): Node value") + testing.expect(t, v == avl.find(&tree, k), "Find(): Node") + testing.expect(t, k == v.value, "Find(): Node value") } // Test the forward/backward iterators. inserted_values: [dynamic]int + defer delete(inserted_values) for k in inserted_map { append(&inserted_values, k) } @@ -60,38 +61,38 @@ test_avl :: proc(t: ^testing.T) { visited: int for node in avl.iterator_next(&iter) { v, idx := node.value, visited - tc.expect(t, inserted_values[idx] == v, "iterator/forward: value") - tc.expect(t, node == avl.iterator_get(&iter), "iterator/forward: get") + testing.expect(t, inserted_values[idx] == v, "iterator/forward: value") + testing.expect(t, node == avl.iterator_get(&iter), "iterator/forward: get") visited += 1 } - tc.expect(t, visited == nrEntries, "iterator/forward: visited") + testing.expect(t, visited == nrEntries, "iterator/forward: visited") slice.reverse(inserted_values[:]) iter = avl.iterator(&tree, avl.Direction.Backward) visited = 0 for node in avl.iterator_next(&iter) { 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 } - tc.expect(t, visited == nrEntries, "iterator/backward: visited") + testing.expect(t, visited == nrEntries, "iterator/backward: visited") // Test removal. rand.shuffle(inserted_values[:], &r) for v, i in inserted_values { 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) - tc.expect(t, ok, "remove: succeeds") - tc.expect(t, nrEntries - (i + 1) == avl.len(&tree), "remove: len (post)") + testing.expect(t, ok, "remove: succeeds") + testing.expect(t, nrEntries - (i + 1) == avl.len(&tree), "remove: len (post)") 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") - tc.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.len(&tree) == 0, "remove: len should be 0") + testing.expect(t, avl.first(&tree) == nil, "remove: first should be nil") + testing.expect(t, avl.last(&tree) == nil, "remove: last should be nil") // Refill the tree. for v in inserted_values { @@ -104,25 +105,25 @@ test_avl :: proc(t: ^testing.T) { v := node.value ok := avl.iterator_remove(&iter) - tc.expect(t, ok, "iterator/remove: success") + testing.expect(t, ok, "iterator/remove: success") 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") - tc.expect(t, avl.iterator_get(&iter) == nil, "iterator/remove: get should return nil") + testing.expect(t, avl.find(&tree, v) == nil, "iterator/remove: node should be gone") + testing.expect(t, avl.iterator_get(&iter) == nil, "iterator/remove: get should return nil") // Ensure that iterator_next still works. node, ok = avl.iterator_next(&iter) - tc.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, ok == (avl.len(&tree) > 0), "iterator/remove: next should return false") + testing.expect(t, node == avl.first(&tree), "iterator/remove: next should return first") 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) - 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) @@ -141,10 +142,10 @@ tree_check_invariants :: proc( } // 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. - tc.expect( + testing.expect( t, node._balance == -1 || node._balance == 0 || node._balance == 1, "invalid balance factor", @@ -155,7 +156,7 @@ tree_check_invariants :: proc( r_height := tree_check_invariants(t, tree, node._right, node) // 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 { return l_height + 1 } diff --git a/tests/core/container/test_core_container.odin b/tests/core/container/test_core_container.odin deleted file mode 100644 index 7dd4a362847..00000000000 --- a/tests/core/container/test_core_container.odin +++ /dev/null @@ -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) -} diff --git a/tests/core/container/test_core_rbtree.odin b/tests/core/container/test_core_rbtree.odin index 89742b1d0d3..8def8edb6ee 100644 --- a/tests/core/container/test_core_rbtree.odin +++ b/tests/core/container/test_core_rbtree.odin @@ -3,14 +3,10 @@ package test_core_container import rb "core:container/rbtree" import "core:math/rand" import "core:testing" -import "core:fmt" import "base:intrinsics" import "core:mem" import "core:slice" -import tc "tests:common" - -RANDOM_SEED :: #config(RANDOM_SEED, 0) -random_seed := u64(intrinsics.read_cycle_counter()) when RANDOM_SEED == 0 else u64(RANDOM_SEED) +import "core:log" test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) { 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) 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) rb.init(&tree) - tc.expect(t, rb.len(&tree) == 0, "empty: len should be 0") - tc.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.len(&tree) == 0, "empty: len should be 0") + testing.expect(t, rb.first(&tree) == nil, "empty: first should be nil") + testing.expect(t, rb.last(&tree) == nil, "empty: last should be nil") 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. 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] 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 { inserted_map[k] = n } 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) - 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) first := rb.first(&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)) - tc.expect(t, last != nil && last.key == max_key, fmt.tprintf("insert: last should be present with key %v", max_key)) + testing.expectf(t, first != nil && first.key == min_key, "insert: first should be present with key %v", min_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. for k, v in inserted_map { - tc.expect(t, v == rb.find(&tree, k), "Find(): Node") - tc.expect(t, k == v.key, "Find(): Node key") + testing.expect(t, v == rb.find(&tree, k), "Find(): Node") + testing.expect(t, k == v.key, "Find(): Node key") } // Test the forward/backward iterators. @@ -79,21 +75,21 @@ test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) { visited: int for node in rb.iterator_next(&iter) { k, idx := node.key, visited - tc.expect(t, inserted_keys[idx] == k, "iterator/forward: key") - tc.expect(t, node == rb.iterator_get(&iter), "iterator/forward: get") + testing.expect(t, inserted_keys[idx] == k, "iterator/forward: key") + testing.expect(t, node == rb.iterator_get(&iter), "iterator/forward: get") visited += 1 } - tc.expect(t, visited == entry_count, "iterator/forward: visited") + testing.expect(t, visited == entry_count, "iterator/forward: visited") slice.reverse(inserted_keys[:]) iter = rb.iterator(&tree, rb.Direction.Backward) visited = 0 for node in rb.iterator_next(&iter) { 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 } - tc.expect(t, visited == entry_count, "iterator/backward: visited") + testing.expect(t, visited == entry_count, "iterator/backward: visited") // Test removal (and on_remove callback) 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 { 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) - tc.expect(t, ok, "remove: succeeds") - tc.expect(t, entry_count - (i + 1) == rb.len(&tree), "remove: len (post)") + testing.expect(t, ok, "remove: succeeds") + testing.expect(t, entry_count - (i + 1) == rb.len(&tree), "remove: len (post)") 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") - tc.expect(t, callback_count == 0, fmt.tprintf("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") - tc.expect(t, rb.last(&tree) == nil, "remove: last should be nil") + testing.expect(t, rb.len(&tree) == 0, "remove: len should be 0") + testing.expectf(t, callback_count == 0, "remove: on_remove should've been called %v times, it was %v", entry_count, callback_count) + testing.expect(t, rb.first(&tree) == nil, "remove: first should be nil") + testing.expect(t, rb.last(&tree) == nil, "remove: last should be nil") // Refill the tree. for k in inserted_keys { @@ -130,32 +126,32 @@ test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) { k := node.key ok := rb.iterator_remove(&iter) - tc.expect(t, ok, "iterator/remove: success") + testing.expect(t, ok, "iterator/remove: success") 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") - tc.expect(t, rb.iterator_get(&iter) == nil, "iterator/remove: get should return nil") + testing.expect(t, rb.find(&tree, k) == nil, "iterator/remove: node should be gone") + testing.expect(t, rb.iterator_get(&iter) == nil, "iterator/remove: get should return nil") // Ensure that iterator_next still works. node, ok = rb.iterator_next(&iter) - tc.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, ok == (rb.len(&tree) > 0), "iterator/remove: next should return false") + testing.expect(t, node == rb.first(&tree), "iterator/remove: next should return first") 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) - tc.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.expect(t, rb.len(&tree) == 0, "destroy: len should be 0") + 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) delete(inserted_map) delete(inserted_keys) - tc.expect(t, len(track.allocation_map) == 0, fmt.tprintf("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.allocation_map) == 0, "Expected 0 leaks, have %v", len(track.allocation_map)) + testing.expectf(t, len(track.bad_free_array) == 0, "Expected 0 bad frees, have %v", len(track.bad_free_array)) 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)) { - 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 { 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)) { - 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)) { if rb.node_color(n) == .Red { // 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 - 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 { 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 { path_black_count^ = black_count } 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 } @@ -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) } // 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. \ No newline at end of file +// which guarantees that it has O(log n) height. diff --git a/tests/core/container/test_core_small_array.odin b/tests/core/container/test_core_small_array.odin index 78998de1660..580df793e70 100644 --- a/tests/core/container/test_core_small_array.odin +++ b/tests/core/container/test_core_small_array.odin @@ -3,44 +3,47 @@ package test_core_container import "core:testing" 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_small_array_removes :: proc(t: ^testing.T) { - array: small_array.Small_Array(10, int) - small_array.append(&array, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) - - small_array.ordered_remove(&array, 0) - expect_equal(t, small_array.slice(&array), []int { 1, 2, 3, 4, 5, 6, 7, 8, 9 }) - small_array.ordered_remove(&array, 5) - expect_equal(t, small_array.slice(&array), []int { 1, 2, 3, 4, 5, 7, 8, 9 }) - small_array.ordered_remove(&array, 6) - expect_equal(t, small_array.slice(&array), []int { 1, 2, 3, 4, 5, 7, 9 }) - small_array.unordered_remove(&array, 0) - expect_equal(t, small_array.slice(&array), []int { 9, 2, 3, 4, 5, 7 }) - small_array.unordered_remove(&array, 2) - expect_equal(t, small_array.slice(&array), []int { 9, 2, 7, 4, 5 }) - small_array.unordered_remove(&array, 4) - expect_equal(t, small_array.slice(&array), []int { 9, 2, 7, 4 }) + array: small_array.Small_Array(10, int) + small_array.append(&array, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) + + small_array.ordered_remove(&array, 0) + 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) + testing.expect(t, slice_equal(small_array.slice(&array), []int { 1, 2, 3, 4, 5, 7, 8, 9 })) + small_array.ordered_remove(&array, 6) + testing.expect(t, slice_equal(small_array.slice(&array), []int { 1, 2, 3, 4, 5, 7, 9 })) + small_array.unordered_remove(&array, 0) + testing.expect(t, slice_equal(small_array.slice(&array), []int { 9, 2, 3, 4, 5, 7 })) + small_array.unordered_remove(&array, 2) + testing.expect(t, slice_equal(small_array.slice(&array), []int { 9, 2, 7, 4, 5 })) + small_array.unordered_remove(&array, 4) + testing.expect(t, slice_equal(small_array.slice(&array), []int { 9, 2, 7, 4 })) } @(test) test_small_array_inject_at :: proc(t: ^testing.T) { - array: small_array.Small_Array(13, int) - small_array.append(&array, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) + array: small_array.Small_Array(13, int) + small_array.append(&array, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) + + testing.expect(t, small_array.inject_at(&array, 0, 0), "Expected to be able to inject into small array") + testing.expect(t, slice_equal(small_array.slice(&array), []int { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 })) + testing.expect(t, small_array.inject_at(&array, 0, 5), "Expected to be able to inject into small array") + testing.expect(t, slice_equal(small_array.slice(&array), []int { 0, 0, 1, 2, 3, 0, 4, 5, 6, 7, 8, 9 })) + testing.expect(t, small_array.inject_at(&array, 0, small_array.len(array)), "Expected to be able to inject into small array") + testing.expect(t, slice_equal(small_array.slice(&array), []int { 0, 0, 1, 2, 3, 0, 4, 5, 6, 7, 8, 9, 0 })) +} - tc.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 }) - tc.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 }) - tc.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 }) +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 } diff --git a/tests/core/crypto/test_core_crypto.odin b/tests/core/crypto/test_core_crypto.odin index 1f7bcece3b4..f3f76646b84 100644 --- a/tests/core/crypto/test_core_crypto.odin +++ b/tests/core/crypto/test_core_crypto.odin @@ -13,41 +13,20 @@ package test_core_crypto */ import "core:encoding/hex" -import "core:fmt" import "core:mem" import "core:testing" +import "base:runtime" +import "core:log" import "core:crypto" import "core:crypto/chacha20" 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." @(test) 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 plaintext := transmute([]byte)(_PLAINTEXT_SUNSCREEN_STR) @@ -90,14 +69,12 @@ test_chacha20 :: proc(t: ^testing.T) { chacha20.xor_bytes(&ctx, derived_ciphertext[:], plaintext[:]) derived_ciphertext_str := string(hex.encode(derived_ciphertext[:], context.temp_allocator)) - tc.expect( + testing.expectf( t, derived_ciphertext_str == ciphertext_str, - fmt.tprintf( - "Expected %s for xor_bytes(plaintext_str), but got %s instead", - ciphertext_str, - derived_ciphertext_str, - ), + "Expected %s for xor_bytes(plaintext_str), but got %s instead", + ciphertext_str, + derived_ciphertext_str, ) xkey := [chacha20.KEY_SIZE]byte { @@ -137,21 +114,17 @@ test_chacha20 :: proc(t: ^testing.T) { chacha20.xor_bytes(&ctx, derived_ciphertext[:], plaintext[:]) derived_ciphertext_str = string(hex.encode(derived_ciphertext[:], context.temp_allocator)) - tc.expect( + testing.expectf( t, derived_ciphertext_str == xciphertext_str, - fmt.tprintf( - "Expected %s for xor_bytes(plaintext_str), but got %s instead", - xciphertext_str, - derived_ciphertext_str, - ), + "Expected %s for xor_bytes(plaintext_str), but got %s instead", + xciphertext_str, + derived_ciphertext_str, ) } @(test) test_chacha20poly1305 :: proc(t: ^testing.T) { - tc.log(t, "Testing chacha20poly1205") - plaintext := transmute([]byte)(_PLAINTEXT_SUNSCREEN_STR) aad := [12]byte { @@ -209,25 +182,21 @@ test_chacha20poly1305 :: proc(t: ^testing.T) { ) derived_ciphertext_str := string(hex.encode(derived_ciphertext[:], context.temp_allocator)) - tc.expect( + testing.expectf( t, derived_ciphertext_str == ciphertext_str, - fmt.tprintf( - "Expected ciphertext %s for encrypt(aad, plaintext), but got %s instead", - ciphertext_str, - derived_ciphertext_str, - ), + "Expected ciphertext %s for encrypt(aad, plaintext), but got %s instead", + ciphertext_str, + derived_ciphertext_str, ) derived_tag_str := string(hex.encode(derived_tag[:], context.temp_allocator)) - tc.expect( + testing.expectf( t, derived_tag_str == tag_str, - fmt.tprintf( - "Expected tag %s for encrypt(aad, plaintext), but got %s instead", - tag_str, - derived_tag_str, - ), + "Expected tag %s for encrypt(aad, plaintext), but got %s instead", + tag_str, + derived_tag_str, ) derived_plaintext: [114]byte @@ -240,15 +209,13 @@ test_chacha20poly1305 :: proc(t: ^testing.T) { ciphertext[:], ) derived_plaintext_str := string(derived_plaintext[:]) - tc.expect(t, ok, "Expected true for decrypt(tag, aad, ciphertext)") - tc.expect( + testing.expect(t, ok, "Expected true for decrypt(tag, aad, ciphertext)") + testing.expectf( t, derived_plaintext_str == _PLAINTEXT_SUNSCREEN_STR, - fmt.tprintf( - "Expected plaintext %s for decrypt(tag, aad, ciphertext), but got %s instead", - _PLAINTEXT_SUNSCREEN_STR, - derived_plaintext_str, - ), + "Expected plaintext %s for decrypt(tag, aad, ciphertext), but got %s instead", + _PLAINTEXT_SUNSCREEN_STR, + derived_plaintext_str, ) derived_ciphertext[0] ~= 0xa5 @@ -260,7 +227,7 @@ test_chacha20poly1305 :: proc(t: ^testing.T) { aad[:], 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 ok = chacha20poly1305.decrypt( @@ -271,15 +238,13 @@ test_chacha20poly1305 :: proc(t: ^testing.T) { aad[:], 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_rand_bytes :: proc(t: ^testing.T) { - tc.log(t, "Testing rand_bytes") - if !crypto.HAS_RAND_BYTES { - tc.log(t, "rand_bytes not supported - skipping") + log.info("rand_bytes not supported - skipping") return } @@ -307,10 +272,5 @@ test_rand_bytes :: proc(t: ^testing.T) { break } } - - tc.expect( - t, - seems_ok, - "Expected to randomize the head and tail of the buffer within a handful of attempts", - ) + testing.expect(t, seems_ok, "Expected to randomize the head and tail of the buffer within a handful of attempts") } diff --git a/tests/core/crypto/test_core_crypto_aes.odin b/tests/core/crypto/test_core_crypto_aes.odin index f33292a53f2..4d4c06bdc9d 100644 --- a/tests/core/crypto/test_core_crypto_aes.odin +++ b/tests/core/crypto/test_core_crypto_aes.odin @@ -2,21 +2,20 @@ package test_core_crypto import "base:runtime" import "core:encoding/hex" -import "core:fmt" +import "core:log" import "core:testing" import "core:crypto/aes" import "core:crypto/sha2" -import tc "tests:common" - @(test) test_aes :: proc(t: ^testing.T) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - tc.log(t, "Testing AES") + log.info("Testing AES") impls := make([dynamic]aes.Implementation, 0, 2) + defer delete(impls) append(&impls, aes.Implementation.Portable) if aes.is_hardware_accelerated() { 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) { - tc.log(t, fmt.tprintf("Testing AES-ECB/%v", impl)) + log.infof("Testing AES-ECB/%v", impl) test_vectors := []struct { key: string, @@ -111,39 +109,34 @@ test_aes_ecb :: proc(t: ^testing.T, impl: aes.Implementation) { aes.encrypt_ecb(&ctx, dst[:], plaintext) dst_str := string(hex.encode(dst[:], context.temp_allocator)) - tc.expect( + testing.expectf( t, dst_str == v.ciphertext, - fmt.tprintf( - "AES-ECB/%v: Expected: %s for encrypt(%s, %s), but got %s instead", - impl, - v.ciphertext, - v.key, - v.plaintext, - dst_str, - ), + "AES-ECB/%v: Expected: %s for encrypt(%s, %s), but got %s instead", + impl, + v.ciphertext, + v.key, + v.plaintext, + dst_str, ) aes.decrypt_ecb(&ctx, dst[:], ciphertext) dst_str = string(hex.encode(dst[:], context.temp_allocator)) - tc.expect( + testing.expectf( t, dst_str == v.plaintext, - fmt.tprintf( - "AES-ECB/%v: Expected: %s for decrypt(%s, %s), but got %s instead", - impl, - v.plaintext, - v.key, - v.ciphertext, - dst_str, - ), + "AES-ECB/%v: Expected: %s for decrypt(%s, %s), but got %s instead", + impl, + v.plaintext, + v.key, + v.ciphertext, + dst_str, ) } } -@(test) 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 { key: string, @@ -185,18 +178,16 @@ test_aes_ctr :: proc(t: ^testing.T, impl: aes.Implementation) { aes.xor_bytes_ctr(&ctx, dst, plaintext) dst_str := string(hex.encode(dst[:], context.temp_allocator)) - tc.expect( + testing.expectf( t, dst_str == v.ciphertext, - fmt.tprintf( - "AES-CTR/%v: Expected: %s for encrypt(%s, %s, %s), but got %s instead", - impl, - v.ciphertext, - v.key, - v.iv, - v.plaintext, - dst_str, - ), + "AES-CTR/%v: Expected: %s for encrypt(%s, %s, %s), but got %s instead", + impl, + v.ciphertext, + v.key, + v.iv, + v.plaintext, + 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)) expected_digest_str := "d4445343afeb9d1237f95b10d00358aed4c1d7d57c9fe480cd0afb5e2ffd448c" - tc.expect( + testing.expectf( t, expected_digest_str == digest_str, - fmt.tprintf( - "AES-CTR/%v: Expected %s for keystream digest, but got %s instead", - impl, - expected_digest_str, - digest_str, - ), + "AES-CTR/%v: Expected %s for keystream digest, but got %s instead", + impl, + expected_digest_str, + digest_str, ) } -@(test) 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 // 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)) tag_str := string(hex.encode(tag_[:], context.temp_allocator)) - tc.expect( + testing.expectf( t, 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", - impl, - v.ciphertext, - v.tag, - v.key, - v.iv, - v.aad, - v.plaintext, - dst_str, - tag_str, - ), + "AES-GCM/%v: Expected: (%s, %s) for seal(%s, %s, %s, %s), but got (%s, %s) instead", + impl, + v.ciphertext, + v.tag, + v.key, + v.iv, + v.aad, + v.plaintext, + dst_str, + tag_str, ) ok := aes.open_gcm(&ctx, dst, iv, aad, ciphertext, tag) dst_str = string(hex.encode(dst[:], context.temp_allocator)) - tc.expect( + testing.expectf( t, 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", - impl, - v.plaintext, - v.key, - v.iv, - v.aad, - v.ciphertext, - v.tag, - dst_str, - ok, - ), + "AES-GCM/%v: Expected: (%s, true) for open(%s, %s, %s, %s, %s), but got (%s, %s) instead", + impl, + v.plaintext, + v.key, + v.iv, + v.aad, + v.ciphertext, + v.tag, + dst_str, + ok, ) } } diff --git a/tests/core/crypto/test_core_crypto_ecc25519.odin b/tests/core/crypto/test_core_crypto_ecc25519.odin index 5ea008f9066..baf4a1a38a3 100644 --- a/tests/core/crypto/test_core_crypto_ecc25519.odin +++ b/tests/core/crypto/test_core_crypto_ecc25519.odin @@ -1,8 +1,6 @@ package test_core_crypto -import "base:runtime" import "core:encoding/hex" -import "core:fmt" import "core:testing" import field "core:crypto/_fiat/field_curve25519" @@ -10,25 +8,8 @@ import "core:crypto/ed25519" import "core:crypto/ristretto255" 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_sqrt_ratio_m1 :: proc(t: ^testing.T) { - tc.log(t, "Testing sqrt_ratio_m1") - test_vectors := []struct { u: string, v: string, @@ -90,25 +71,21 @@ test_sqrt_ratio_m1 :: proc(t: ^testing.T) { field.fe_relax_cast(&vee), ) - tc.expect( + testing.expectf( t, (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", - v.was_square, - v.r, - v.u, - v.v, - fe_str(&r), - ), + "Expected (%v, %s) for SQRT_RATIO_M1(%s, %s), got %s", + v.was_square, + v.r, + v.u, + v.v, + fe_str(&r), ) } } @(test) test_ristretto255 :: proc(t: ^testing.T) { - tc.log(t, "Testing ristretto255") - ge_gen: ristretto255.Group_Element ristretto255.ge_generator(&ge_gen) @@ -158,7 +135,7 @@ test_ristretto255 :: proc(t: ^testing.T) { ge: ristretto255.Group_Element 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 { @@ -185,22 +162,20 @@ test_ristretto255 :: proc(t: ^testing.T) { ge := &ges[i] 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) - tc.expect( + testing.expectf( t, x == x_check, - fmt.tprintf( - "Expected %s (round-trip) but got %s instead", - x, - x_check, - ), + "Expected %s (round-trip) but got %s instead", + x, + x_check, ) if i == 1 { - tc.expect( + testing.expect( t, ristretto255.ge_equal(ge, &ge_gen) == 1, "Expected element 1 to be the generator", @@ -217,41 +192,35 @@ test_ristretto255 :: proc(t: ^testing.T) { ristretto255.ge_scalarmult_generator(&ge_check, &sc) x_check := ge_str(&ge_check) - tc.expect( + testing.expectf( t, x_check == generator_multiples[i], - fmt.tprintf( - "Expected %s for G * %d (specialized), got %s", - generator_multiples[i], - i, - x_check, - ), + "Expected %s for G * %d (specialized), got %s", + generator_multiples[i], + i, + x_check, ) ristretto255.ge_scalarmult(&ge_check, &ges[1], &sc) x_check = ge_str(&ge_check) - tc.expect( + testing.expectf( t, x_check == generator_multiples[i], - fmt.tprintf( - "Expected %s for G * %d (generic), got %s (slow compare)", - generator_multiples[i], - i, - x_check, - ), + "Expected %s for G * %d (generic), got %s (slow compare)", + generator_multiples[i], + i, + x_check, ) ristretto255.ge_scalarmult_vartime(&ge_check, &ges[1], &sc) x_check = ge_str(&ge_check) - tc.expect( + testing.expectf( t, x_check == generator_multiples[i], - fmt.tprintf( - "Expected %s for G * %d (generic vartime), got %s (slow compare)", - generator_multiples[i], - i, - x_check, - ), + "Expected %s for G * %d (generic vartime), got %s (slow compare)", + generator_multiples[i], + i, + x_check, ) switch i { @@ -261,28 +230,24 @@ test_ristretto255 :: proc(t: ^testing.T) { ristretto255.ge_add(&ge_check, ge_prev, &ge_gen) x_check = ge_str(&ge_check) - tc.expect( + testing.expectf( t, x_check == generator_multiples[i], - fmt.tprintf( - "Expected %s for ges[%d] + ges[%d], got %s (slow compare)", - generator_multiples[i], - i-1, - 1, - x_check, - ), + "Expected %s for ges[%d] + ges[%d], got %s (slow compare)", + generator_multiples[i], + i-1, + 1, + x_check, ) - tc.expect( + testing.expectf( t, ristretto255.ge_equal(&ges[i], &ge_check) == 1, - fmt.tprintf( - "Expected %s for ges[%d] + ges[%d], got %s (fast compare)", - generator_multiples[i], - i-1, - 1, - x_check, - ), + "Expected %s for ges[%d] + ges[%d], got %s (fast compare)", + generator_multiples[i], + i-1, + 1, + x_check, ) } } @@ -344,22 +309,18 @@ test_ristretto255 :: proc(t: ^testing.T) { ristretto255.ge_set_wide_bytes(&ge, in_bytes) ge_check := ge_str(&ge) - tc.expect( + testing.expectf( t, ge_check == v.output, - fmt.tprintf( - "Expected %s for %s, got %s", - v.output, - ge_check, - ), + "Expected %s for %s, got %s", + v.output, + ge_check, ) } } @(test) test_ed25519 :: proc(t: ^testing.T) { - tc.log(t, "Testing ed25519") - test_vectors_rfc := []struct { priv_key: string, pub_key: string, @@ -401,87 +362,73 @@ test_ed25519 :: proc(t: ^testing.T) { priv_key: ed25519.Private_Key ok := ed25519.private_key_set_bytes(&priv_key, priv_bytes) - tc.expect( + testing.expectf( t, ok, - fmt.tprintf( - "Expected %s to be a valid private key", - v.priv_key, - ), + "Expected %s to be a valid private key", + v.priv_key, ) key_bytes: [32]byte ed25519.private_key_bytes(&priv_key, key_bytes[:]) - tc.expect( + testing.expectf( t, ok, - fmt.tprintf( - "Expected private key %s round-trip, got %s", - v.priv_key, - string(hex.encode(key_bytes[:], context.temp_allocator)), - ), + "Expected private key %s round-trip, got %s", + v.priv_key, + string(hex.encode(key_bytes[:], context.temp_allocator)), ) pub_key: ed25519.Public_Key ok = ed25519.public_key_set_bytes(&pub_key, pub_bytes) - tc.expect( + testing.expectf( t, ok, - fmt.tprintf( - "Expected %s to be a valid public key (priv->pub: %s)", - v.pub_key, - string(hex.encode(priv_key._pub_key._b[:], context.temp_allocator)), - ), + "Expected %s to be a valid public key (priv->pub: %s)", + v.pub_key, + string(hex.encode(priv_key._pub_key._b[:], context.temp_allocator)), ) ed25519.public_key_bytes(&pub_key, key_bytes[:]) - tc.expect( + testing.expectf( t, ok, - fmt.tprintf( - "Expected public key %s round-trip, got %s", - v.pub_key, - string(hex.encode(key_bytes[:], context.temp_allocator)), - ), + "Expected public key %s round-trip, got %s", + v.pub_key, + string(hex.encode(key_bytes[:], context.temp_allocator)), ) sig: [ed25519.SIGNATURE_SIZE]byte ed25519.sign(&priv_key, msg_bytes, sig[:]) x := string(hex.encode(sig[:], context.temp_allocator)) - tc.expect( + testing.expectf( t, x == v.sig, - fmt.tprintf( - "Expected %s for sign(%s, %s), got %s", - v.sig, - v.priv_key, - v.msg, - x, - ), + "Expected %s for sign(%s, %s), got %s", + v.sig, + v.priv_key, + v.msg, + x, ) ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes) - tc.expect( + testing.expectf( t, ok, - fmt.tprintf( - "Expected true for verify(%s, %s, %s)", - v.pub_key, - v.msg, - v.sig, - ), + "Expected true for verify(%s, %s, %s)", + v.pub_key, + v.msg, + v.sig, ) ok = ed25519.verify(&priv_key._pub_key, msg_bytes, sig_bytes) - tc.expect( + testing.expectf( t, ok, - fmt.tprintf( - "Expected true for verify(pub(%s), %s %s)", - v.priv_key, - v.msg, - v.sig, - ), + "Expected true for verify(pub(%s), %s %s)", + v.priv_key, + v.msg, + v.sig, ) // 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 } ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes) - tc.expect( + testing.expectf( t, ok == false, - fmt.tprintf( - "Expected false for verify(%s, %s (corrupted), %s)", - v.pub_key, - v.msg, - v.sig, - ), + "Expected false for verify(%s, %s (corrupted), %s)", + v.pub_key, + v.msg, + v.sig, ) } @@ -634,15 +579,13 @@ test_ed25519 :: proc(t: ^testing.T) { pub_key: ed25519.Public_Key ok := ed25519.public_key_set_bytes(&pub_key, pub_bytes) - tc.expect( + testing.expectf( t, ok == v.pub_key_ok, - fmt.tprintf( - "speccheck/%d: Expected %s to be a (in)valid public key, got %v", - i, - v.pub_key, - ok, - ), + "speccheck/%d: Expected %s to be a (in)valid public key, got %v", + i, + v.pub_key, + ok, ) // 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) - tc.expect( + testing.expectf( t, ok == v.sig_ok, - fmt.tprintf( - "speccheck/%d Expected %v for verify(%s, %s, %s)", - i, - v.sig_ok, - v.pub_key, - v.msg, - v.sig, - ), + "speccheck/%d Expected %v for verify(%s, %s, %s)", + i, + v.sig_ok, + v.pub_key, + v.msg, + v.sig, ) // 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) - tc.expect( + testing.expectf( t, ok == v.sig_ok_relaxed, - fmt.tprintf( - "speccheck/%d Expected %v for verify(%s, %s, %s, true)", - i, - v.sig_ok_relaxed, - v.pub_key, - v.msg, - v.sig, - ), + "speccheck/%d Expected %v for verify(%s, %s, %s, true)", + i, + v.sig_ok_relaxed, + v.pub_key, + v.msg, + v.sig, ) } } @(test) 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. _BASE_POINT: [32]byte = { 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[:]) derived_point_str := string(hex.encode(derived_point[:], context.temp_allocator)) - tc.expect( + testing.expectf( t, derived_point_str == v.product, - fmt.tprintf( - "Expected %s for %s * %s, but got %s instead", - v.product, - v.scalar, - v.point, - derived_point_str, - ), - ) + "Expected %s for %s * %s, but got %s instead", + v.product, + v.scalar, + v.point, + derived_point_str, + ) // Abuse the test vectors to sanity-check the scalar-basepoint multiply. p1, p2: [x25519.POINT_SIZE]byte @@ -738,15 +673,13 @@ test_x25519 :: proc(t: ^testing.T) { x25519.scalarmult(p2[:], scalar[:], _BASE_POINT[:]) p1_str := string(hex.encode(p1[:], context.temp_allocator)) p2_str := string(hex.encode(p2[:], context.temp_allocator)) - tc.expect( + testing.expectf( t, p1_str == p2_str, - fmt.tprintf( - "Expected %s for %s * basepoint, but got %s instead", - p2_str, - v.scalar, - p1_str, - ), + "Expected %s for %s * basepoint, but got %s instead", + p2_str, + v.scalar, + p1_str, ) } } @@ -763,4 +696,4 @@ fe_str :: proc(fe: ^field.Tight_Field_Element) -> string { b: [32]byte field.fe_to_bytes(&b, fe) return string(hex.encode(b[:], context.temp_allocator)) -} +} \ No newline at end of file diff --git a/tests/core/crypto/test_core_crypto_hash.odin b/tests/core/crypto/test_core_crypto_hash.odin index c4e8e8dd7e6..9a9d0cc769d 100644 --- a/tests/core/crypto/test_core_crypto_hash.odin +++ b/tests/core/crypto/test_core_crypto_hash.odin @@ -3,23 +3,17 @@ package test_core_crypto import "base:runtime" import "core:bytes" import "core:encoding/hex" -import "core:fmt" import "core:strings" import "core:testing" - import "core:crypto/hash" -import tc "tests:common" - @(test) test_hash :: proc(t: ^testing.T) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - tc.log(t, "Testing Hashes") - // TODO: // - 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 test_vectors := []struct{ @@ -496,16 +490,14 @@ test_hash :: proc(t: ^testing.T) { dst_str := string(hex.encode(dst, context.temp_allocator)) - tc.expect( + testing.expectf( t, dst_str == v.hash, - fmt.tprintf( - "%s/incremental: Expected: %s for input of %s, but got %s instead", - algo_name, - v.hash, - v.str, - dst_str, - ), + "%s/incremental: Expected: %s for input of %s, but got %s instead", + algo_name, + v.hash, + v.str, + dst_str, ) } @@ -521,25 +513,21 @@ test_hash :: proc(t: ^testing.T) { // still correct. digest_sz := hash.DIGEST_SIZES[algo] block_sz := hash.BLOCK_SIZES[algo] - tc.expect( + testing.expectf( t, digest_sz <= hash.MAX_DIGEST_SIZE, - fmt.tprintf( - "%s: Digest size %d exceeds max %d", - algo_name, - digest_sz, - hash.MAX_DIGEST_SIZE, - ), + "%s: Digest size %d exceeds max %d", + algo_name, + digest_sz, + hash.MAX_DIGEST_SIZE, ) - tc.expect( + testing.expectf( t, block_sz <= hash.MAX_BLOCK_SIZE, - fmt.tprintf( - "%s: Block size %d exceeds max %d", - algo_name, - block_sz, - hash.MAX_BLOCK_SIZE, - ), + "%s: Block size %d exceeds max %d", + algo_name, + block_sz, + hash.MAX_BLOCK_SIZE, ) // 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)) b_str := string(hex.encode(digest_b, context.temp_allocator)) - tc.expect( + testing.expectf( t, a_str == b_str, - fmt.tprintf( - "%s/cmp: Expected: %s (hash_stream) == %s (hash_bytes)", - algo_name, - a_str, - b_str, - ), + "%s/cmp: Expected: %s (hash_stream) == %s (hash_bytes)", + algo_name, + a_str, + b_str, ) // Exercise the rolling digest functionality, which also covers @@ -571,25 +557,21 @@ test_hash :: proc(t: ^testing.T) { api_algo := hash.algorithm(&ctx) api_digest_size := hash.digest_size(&ctx) - tc.expect( + testing.expectf( t, algo == api_algo, - fmt.tprintf( - "%s/algorithm: Expected: %v but got %v instead", - algo_name, - algo, - api_algo, - ), + "%s/algorithm: Expected: %v but got %v instead", + algo_name, + algo, + api_algo, ) - tc.expect( + testing.expectf( t, hash.DIGEST_SIZES[algo] == api_digest_size, - fmt.tprintf( - "%s/digest_size: Expected: %d but got %d instead", - algo_name, - hash.DIGEST_SIZES[algo], - api_digest_size, - ), + "%s/digest_size: Expected: %d but got %d instead", + algo_name, + hash.DIGEST_SIZES[algo], + api_digest_size, ) 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)) c_str := string(hex.encode(digest_c, context.temp_allocator)) - tc.expect( + testing.expectf( t, a_str == b_str && b_str == c_str, - fmt.tprintf( - "%s/rolling: Expected: %s (first) == %s (second) == %s (third)", - algo_name, - a_str, - b_str, - c_str, - ), + "%s/rolling: Expected: %s (first) == %s (second) == %s (third)", + algo_name, + a_str, + b_str, + c_str, ) } -} +} \ No newline at end of file diff --git a/tests/core/crypto/test_core_crypto_kdf.odin b/tests/core/crypto/test_core_crypto_kdf.odin index 73177d8be9d..247529e652e 100644 --- a/tests/core/crypto/test_core_crypto_kdf.odin +++ b/tests/core/crypto/test_core_crypto_kdf.odin @@ -2,28 +2,14 @@ package test_core_crypto import "base:runtime" import "core:encoding/hex" -import "core:fmt" import "core:testing" - import "core:crypto/hash" import "core:crypto/hkdf" 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_hkdf :: proc(t: ^testing.T) { - tc.log(t, "Testing HKDF") + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() tmp: [128]byte // Good enough. @@ -70,25 +56,23 @@ test_hkdf :: proc(t: ^testing.T) { dst_str := string(hex.encode(dst, context.temp_allocator)) - tc.expect( + testing.expectf( t, dst_str == v.okm, - fmt.tprintf( - "HKDF-%s: Expected: %s for input of (%s, %s, %s), but got %s instead", - algo_name, - v.okm, - v.ikm, - v.salt, - v.info, - dst_str, - ), + "HKDF-%s: Expected: %s for input of (%s, %s, %s), but got %s instead", + algo_name, + v.okm, + v.ikm, + v.salt, + v.info, + dst_str, ) } } @(test) 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. @@ -174,18 +158,16 @@ test_pbkdf2 :: proc(t: ^testing.T) { dst_str := string(hex.encode(dst, context.temp_allocator)) - tc.expect( + testing.expectf( t, dst_str == v.dk, - fmt.tprintf( - "HMAC-%s: Expected: %s for input of (%s, %s, %d), but got %s instead", - algo_name, - v.dk, - v.password, - v.salt, - v.iterations, - dst_str, - ), + "HMAC-%s: Expected: %s for input of (%s, %s, %d), but got %s instead", + algo_name, + v.dk, + v.password, + v.salt, + v.iterations, + dst_str, ) } } diff --git a/tests/core/crypto/test_core_crypto_mac.odin b/tests/core/crypto/test_core_crypto_mac.odin index f2eeacb1999..ed95ba0ad1c 100644 --- a/tests/core/crypto/test_core_crypto_mac.odin +++ b/tests/core/crypto/test_core_crypto_mac.odin @@ -2,30 +2,17 @@ package test_core_crypto import "base:runtime" import "core:encoding/hex" -import "core:fmt" import "core:mem" import "core:testing" - import "core:crypto/hash" import "core:crypto/hmac" import "core:crypto/poly1305" import "core:crypto/siphash" -import tc "tests:common" - @(test) -test_mac :: proc(t: ^testing.T) { +test_hmac :: 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_hmac :: proc(t: ^testing.T) { // 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 // 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)) dst_str := string(hex.encode(dst[:tag_len], context.temp_allocator)) - tc.expect( + testing.expectf( t, dst_str == expected_str, - fmt.tprintf( - "%s/incremental: Expected: %s for input of %s - %s, but got %s instead", - algo_name, - tags_sha256[i], - key_str, - msg_str, - dst_str, - ), + "%s/incremental: Expected: %s for input of %s - %s, but got %s instead", + algo_name, + tags_sha256[i], + key_str, + msg_str, + dst_str, ) hmac.sum(algo, dst, msg, key) oneshot_str := string(hex.encode(dst[:tag_len], context.temp_allocator)) - tc.expect( + testing.expectf( t, oneshot_str == expected_str, - fmt.tprintf( - "%s/oneshot: Expected: %s for input of %s - %s, but got %s instead", - algo_name, - tags_sha256[i], - key_str, - msg_str, - oneshot_str, - ), + "%s/oneshot: Expected: %s for input of %s - %s, but got %s instead", + algo_name, + tags_sha256[i], + key_str, + msg_str, + oneshot_str, ) } } @(test) test_poly1305 :: proc(t: ^testing.T) { - tc.log(t, "Testing poly1305") + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() // Test cases taken from poly1305-donna. key := [poly1305.KEY_SIZE]byte { @@ -157,16 +140,17 @@ test_poly1305 :: proc(t: ^testing.T) { // Verify - oneshot + compare ok := poly1305.verify(tag[:], msg[:], key[:]) - tc.expect(t, ok, "oneshot verify call failed") + testing.expect(t, ok, "oneshot verify call failed") // Sum - oneshot derived_tag: [poly1305.TAG_SIZE]byte poly1305.sum(derived_tag[:], msg[:], key[:]) derived_tag_str := string(hex.encode(derived_tag[:], context.temp_allocator)) - tc.expect( + testing.expectf( t, 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 @@ -182,21 +166,16 @@ test_poly1305 :: proc(t: ^testing.T) { } poly1305.final(&ctx, derived_tag[:]) derived_tag_str = string(hex.encode(derived_tag[:], context.temp_allocator)) - tc.expect( + testing.expectf( t, derived_tag_str == tag_str, - fmt.tprintf( - "Expected %s for init/update/final - incremental, but got %s instead", - tag_str, - derived_tag_str, - ), + "Expected %s for init/update/final - incremental, but got %s instead", + tag_str, derived_tag_str, ) } @(test) test_siphash_2_4 :: proc(t: ^testing.T) { - tc.log(t, "Testing SipHash-2-4") - // Test vectors from // https://github.com/veorq/SipHash/blob/master/vectors.h test_vectors := [?]u64 { @@ -225,6 +204,7 @@ test_siphash_2_4 :: proc(t: ^testing.T) { for i in 0 ..< len(test_vectors) { data := make([]byte, i) + defer delete(data) for j in 0 ..< i { data[j] = byte(j) } @@ -232,15 +212,13 @@ test_siphash_2_4 :: proc(t: ^testing.T) { vector := test_vectors[i] computed := siphash.sum_2_4(data[:], key[:]) - tc.expect( + testing.expectf( t, computed == vector, - fmt.tprintf( - "Expected: 0x%x for input of %v, but got 0x%x instead", - vector, - data, - computed, - ), + "Expected: 0x%x for input of %v, but got 0x%x instead", + vector, + data, + computed, ) } } diff --git a/tests/core/crypto/test_core_crypto_sha3_variants.odin b/tests/core/crypto/test_core_crypto_sha3_variants.odin index 8e44996bc0a..c11868e72a4 100644 --- a/tests/core/crypto/test_core_crypto_sha3_variants.odin +++ b/tests/core/crypto/test_core_crypto_sha3_variants.odin @@ -2,30 +2,14 @@ package test_core_crypto import "base:runtime" import "core:encoding/hex" -import "core:fmt" import "core:testing" - import "core:crypto/kmac" import "core:crypto/shake" 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_shake :: proc(t: ^testing.T) { - tc.log(t, "Testing SHAKE") + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() test_vectors := []struct { sec_strength: int, @@ -67,23 +51,21 @@ test_shake :: proc(t: ^testing.T) { dst_str := string(hex.encode(dst, context.temp_allocator)) - tc.expect( + testing.expectf( t, dst_str == v.output, - fmt.tprintf( - "SHAKE%d: Expected: %s for input of %s, but got %s instead", - v.sec_strength, - v.output, - v.str, - dst_str, - ), + "SHAKE%d: Expected: %s for input of %s, but got %s instead", + v.sec_strength, + v.output, + v.str, + dst_str, ) } } @(test) test_cshake :: proc(t: ^testing.T) { - tc.log(t, "Testing cSHAKE") + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() test_vectors := []struct { sec_strength: int, @@ -135,29 +117,27 @@ test_cshake :: proc(t: ^testing.T) { 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.read(&ctx, dst) dst_str := string(hex.encode(dst, context.temp_allocator)) - tc.expect( + testing.expectf( t, dst_str == v.output, - fmt.tprintf( - "cSHAKE%d: Expected: %s for input of %s, but got %s instead", - v.sec_strength, - v.output, - v.str, - dst_str, - ), + "cSHAKE%d: Expected: %s for input of %s, but got %s instead", + v.sec_strength, + v.output, + v.str, + dst_str, ) } } @(test) test_tuplehash :: proc(t: ^testing.T) { - tc.log(t, "Testing TupleHash(XOF)") + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() test_vectors := []struct { sec_strength: int, @@ -317,7 +297,7 @@ test_tuplehash :: proc(t: ^testing.T) { } 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) } @@ -332,24 +312,22 @@ test_tuplehash :: proc(t: ^testing.T) { dst_str := string(hex.encode(dst, context.temp_allocator)) - tc.expect( + testing.expectf( t, dst_str == v.output, - fmt.tprintf( - "TupleHash%s%d: Expected: %s for input of %v, but got %s instead", - suffix, - v.sec_strength, - v.output, - v.tuple, - dst_str, - ), + "TupleHash%s%d: Expected: %s for input of %v, but got %s instead", + suffix, + v.sec_strength, + v.output, + v.tuple, + dst_str, ) } } @(test) test_kmac :: proc(t:^testing.T) { - tc.log(t, "Testing KMAC") + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() test_vectors := []struct { sec_strength: int, @@ -410,7 +388,7 @@ test_kmac :: proc(t:^testing.T) { for v in test_vectors { 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) ctx: kmac.Context @@ -421,24 +399,22 @@ test_kmac :: proc(t:^testing.T) { 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.final(&ctx, dst) dst_str := string(hex.encode(dst, context.temp_allocator)) - tc.expect( + testing.expectf( t, dst_str == v.output, - fmt.tprintf( - "KMAC%d: Expected: %s for input of (%s, %s, %s), but got %s instead", - v.sec_strength, - v.output, - v.key, - v.domainsep, - v.msg, - dst_str, - ), + "KMAC%d: Expected: %s for input of (%s, %s, %s), but got %s instead", + v.sec_strength, + v.output, + v.key, + v.domainsep, + v.msg, + dst_str, ) } -} +} \ No newline at end of file diff --git a/tests/core/crypto/test_crypto_benchmark.odin b/tests/core/crypto/test_crypto_benchmark.odin deleted file mode 100644 index 600dc9adeec..00000000000 --- a/tests/core/crypto/test_crypto_benchmark.odin +++ /dev/null @@ -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), - ) -} diff --git a/tests/core/encoding/base64/base64.odin b/tests/core/encoding/base64/base64.odin index e48eea02064..6679c8ce25d 100644 --- a/tests/core/encoding/base64/base64.odin +++ b/tests/core/encoding/base64/base64.odin @@ -1,61 +1,38 @@ package test_encoding_base64 import "base:intrinsics" - import "core:encoding/base64" -import "core:fmt" -import "core:os" -import "core:reflect" 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 - } +Test :: struct { + vector: string, + base64: string, } -main :: proc() { - t := testing.T{} - - test_encoding(&t) - test_decoding(&t) - - fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) - if TEST_fail > 0 { - os.exit(1) - } +tests :: []Test{ + {"", ""}, + {"f", "Zg=="}, + {"fo", "Zm8="}, + {"foo", "Zm9v"}, + {"foob", "Zm9vYg=="}, + {"fooba", "Zm9vYmE="}, + {"foobar", "Zm9vYmFy"}, } @(test) test_encoding :: proc(t: ^testing.T) { - expect_value(t, base64.encode(transmute([]byte)string("")), "") - expect_value(t, base64.encode(transmute([]byte)string("f")), "Zg==") - expect_value(t, base64.encode(transmute([]byte)string("fo")), "Zm8=") - expect_value(t, base64.encode(transmute([]byte)string("foo")), "Zm9v") - 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") + for test in tests { + v := base64.encode(transmute([]byte)test.vector) + defer delete(v) + testing.expect_value(t, v, test.base64) + } } @(test) test_decoding :: proc(t: ^testing.T) { - expect_value(t, string(base64.decode("")), "") - expect_value(t, string(base64.decode("Zg==")), "f") - expect_value(t, string(base64.decode("Zm8=")), "fo") - expect_value(t, string(base64.decode("Zm9v")), "foo") - expect_value(t, string(base64.decode("Zm9vYg==")), "foob") - expect_value(t, string(base64.decode("Zm9vYmE=")), "fooba") - expect_value(t, string(base64.decode("Zm9vYmFy")), "foobar") + for test in tests { + v := string(base64.decode(test.base64)) + defer delete(v) + testing.expect_value(t, v, test.vector) + } } diff --git a/tests/core/encoding/cbor/test_core_cbor.odin b/tests/core/encoding/cbor/test_core_cbor.odin index 72244e1d332..d069ef05bb7 100644 --- a/tests/core/encoding/cbor/test_core_cbor.odin +++ b/tests/core/encoding/cbor/test_core_cbor.odin @@ -1,105 +1,15 @@ package test_encoding_cbor import "base:intrinsics" - import "core:bytes" import "core:encoding/cbor" import "core:fmt" import "core:io" import "core:math/big" -import "core:mem" -import "core:os" import "core:reflect" import "core:testing" 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 { str: string, cstr: cstring, @@ -143,14 +53,6 @@ FooBars :: bit_set[FooBar; u16] @(test) 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" 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) - ev(t, err, nil) + testing.expect_value(t, err, nil) defer delete(data) decoded, derr := cbor.decode(string(data)) - ev(t, derr, nil) + testing.expect_value(t, derr, nil) defer cbor.destroy(decoded) diagnosis, eerr := cbor.to_diagnostic_format(decoded) - ev(t, eerr, nil) + testing.expect_value(t, eerr, nil) defer delete(diagnosis) - ev(t, diagnosis, `{ + testing.expect_value(t, diagnosis, `{ "base64": 34("MTYgaXMgYSBuaWNlIG51bWJlcg=="), "biggest": 2(h'f951a9fd3c158afdff08ab8e0'), "biggie": 18446744073709551615, @@ -285,7 +187,7 @@ test_marshalling :: proc(t: ^testing.T) { backf: Foo uerr := cbor.unmarshal(string(data), &backf) - ev(t, uerr, nil) + testing.expect_value(t, uerr, nil) defer { delete(backf.str) delete(backf.cstr) @@ -304,104 +206,102 @@ test_marshalling :: proc(t: ^testing.T) { big.destroy(&backf.smallest) } - ev(t, backf.str, f.str) - ev(t, backf.cstr, f.cstr) + testing.expect_value(t, backf.str, f.str) + testing.expect_value(t, backf.cstr, f.cstr) #partial switch v in backf.value { case ^cbor.Map: for entry, i in v { 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 { - ev(t, str^, fm[i].value.(^cbor.Text)^) + testing.expect_value(t, str^, fm[i].value.(^cbor.Text)^) } 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) - ev(t, backf.iamint, f.iamint) - ev(t, backf.base64, f.base64) - ev(t, backf.renamed, f.renamed) - ev(t, backf.now, f.now) - ev(t, backf.nowie, f.nowie) - for e, i in f.child.dyn { ev(t, backf.child.dyn[i], e) } - for key, value in f.child.mappy { ev(t, backf.child.mappy[key], value) } - ev(t, backf.child.my_integers, f.child.my_integers) - ev(t, len(backf.my_bytes), 0) - ev(t, len(backf.my_bytes), len(f.my_bytes)) - ev(t, backf.ennie, f.ennie) - ev(t, backf.ennieb, f.ennieb) - ev(t, backf.quat, f.quat) - ev(t, backf.comp, f.comp) - ev(t, backf.important, f.important) - ev(t, backf.no, nil) - ev(t, backf.nos, nil) - ev(t, backf.yes, f.yes) - ev(t, backf.biggie, f.biggie) - ev(t, backf.smallie, f.smallie) - ev(t, backf.onetwenty, f.onetwenty) - ev(t, backf.small_onetwenty, f.small_onetwenty) - ev(t, backf.ignore_this, nil) + testing.expect_value(t, backf.neg, f.neg) + testing.expect_value(t, backf.iamint, f.iamint) + testing.expect_value(t, backf.base64, f.base64) + testing.expect_value(t, backf.renamed, f.renamed) + testing.expect_value(t, backf.now, f.now) + testing.expect_value(t, backf.nowie, f.nowie) + for e, i in f.child.dyn { testing.expect_value(t, backf.child.dyn[i], e) } + for key, value in f.child.mappy { testing.expect_value(t, backf.child.mappy[key], value) } + testing.expect_value(t, backf.child.my_integers, f.child.my_integers) + testing.expect_value(t, len(backf.my_bytes), 0) + testing.expect_value(t, len(backf.my_bytes), len(f.my_bytes)) + testing.expect_value(t, backf.ennie, f.ennie) + testing.expect_value(t, backf.ennieb, f.ennieb) + testing.expect_value(t, backf.quat, f.quat) + testing.expect_value(t, backf.comp, f.comp) + testing.expect_value(t, backf.important, f.important) + testing.expect_value(t, backf.no, nil) + testing.expect_value(t, backf.nos, nil) + testing.expect_value(t, backf.yes, f.yes) + testing.expect_value(t, backf.biggie, f.biggie) + testing.expect_value(t, backf.smallie, f.smallie) + testing.expect_value(t, backf.onetwenty, f.onetwenty) + testing.expect_value(t, backf.small_onetwenty, f.small_onetwenty) + testing.expect_value(t, backf.ignore_this, nil) 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 { - 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) - ev(t, b_err, nil) + testing.expect_value(t, b_err, nil) 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_marshalling_maybe :: proc(t: ^testing.T) { maybe_test: Maybe(int) = 1 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)) - 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) uerr := cbor.unmarshal(string(data), &maybe_dest) - expect_value(t, uerr, nil) - expect_value(t, maybe_dest, 1) + testing.expect_value(t, uerr, nil) + testing.expect_value(t, maybe_dest, 1) } @(test) test_marshalling_nil_maybe :: proc(t: ^testing.T) { maybe_test: Maybe(int) 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)) - 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) uerr := cbor.unmarshal(string(data), &maybe_dest) - expect_value(t, uerr, nil) - expect_value(t, maybe_dest, nil) + testing.expect_value(t, uerr, nil) + testing.expect_value(t, maybe_dest, nil) } @(test) @@ -427,17 +327,24 @@ test_marshalling_union :: proc(t: ^testing.T) { { test: My_Union = My_Distinct("Hello, World!") 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)) - 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 uerr := cbor.unmarshal(string(data), &dest) - expect_value(t, uerr, nil) - expect_value(t, dest, My_Distinct("Hello, World!")) + testing.expect_value(t, uerr, nil) + 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 { @@ -450,17 +357,21 @@ test_marshalling_union :: proc(t: ^testing.T) { { test: My_Union_No_Nil = My_Struct{.Two} 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)) - 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 uerr := cbor.unmarshal(string(data), &dest) - expect_value(t, uerr, nil) - expect_value(t, dest, My_Struct{.Two}) + testing.expect_value(t, uerr, nil) + 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 := []byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42} _, 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) @@ -691,65 +602,73 @@ test_encode_lists :: proc(t: ^testing.T) { 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_stream_begin(stream, .Array) - expect_value(t, err, nil) + testing.expect_value(t, err, nil) { 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)}) - expect_value(t, err, nil) + testing.expect_value(t, err, nil) 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)) - expect_value(t, err, nil) + testing.expect_value(t, err, nil) 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) - expect_value(t, err, nil) + testing.expect_value(t, err, nil) } 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_u8(stream, 2, .Array) - expect_value(t, err, nil) + testing.expect_value(t, err, nil) a := "a" err = cbor.encode(encoder, &a) - expect_value(t, err, nil) + testing.expect_value(t, err, nil) { err = cbor.encode_stream_begin(stream, .Map) - expect_value(t, err, nil) + testing.expect_value(t, err, nil) b := "b" c := "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) - 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) defer cbor.destroy(res) - expect_value(t, reflect.union_variant_typeid(res), type, loc) - expect_value(t, err, nil, loc) + testing.expect_value(t, reflect.union_variant_typeid(res), type, loc) + testing.expect_value(t, err, nil, loc) str := cbor.to_diagnostic_format(res, padding=-1) 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) { res, err := cbor.decode(encoded) 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 { - expect_value(t, tag.number, nr, loc) + testing.expect_value(t, tag.number, nr, loc) str := cbor.to_diagnostic_format(tag, padding=-1) defer delete(str) - expect_value(t, str, value_decoded, loc) + testing.expect_value(t, str, value_decoded, loc) } 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) defer cbor.destroy(res) - expect_value(t, reflect.union_variant_typeid(res), typeid_of(T), loc) - expect_value(t, err, nil, loc) + testing.expect_value(t, reflect.union_variant_typeid(res), typeid_of(T), loc) + testing.expect_value(t, err, nil, loc) #partial switch r in res { 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: - 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: - when T == f64 { expect_value(t, res, expected, loc) } else { unreachable() } + when T == f64 { testing.expect_value(t, res, expected, loc) } else { unreachable() } case: 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) { - 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) - expect_value(t, err, nil, loc) - expect_value(t, fmt.tprint(bytes.buffer_to_bytes(&buf)), fmt.tprint(transmute([]byte)encoded), loc) + err := cbor.encode(encoder, val, loc) + testing.expect_value(t, err, nil, 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) { - 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 { err: cbor.Encode_Error @@ -891,15 +814,15 @@ expect_streamed_encoding :: proc(t: ^testing.T, encoded: string, values: ..cbor. if err2 != nil { break } } 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) - expect_value(t, err2, nil, loc) + testing.expect_value(t, err, nil, loc) + testing.expect_value(t, err2, nil, loc) } 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) } diff --git a/tests/core/encoding/hex/test_core_hex.odin b/tests/core/encoding/hex/test_core_hex.odin index d928cd28edc..6a00c970597 100644 --- a/tests/core/encoding/hex/test_core_hex.odin +++ b/tests/core/encoding/hex/test_core_hex.odin @@ -2,42 +2,6 @@ package test_core_hex import "core:encoding/hex" 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{ {"11", "3131"}, @@ -49,10 +13,14 @@ CASES :: [][2]string{ hex_encode :: proc(t: ^testing.T) { for test in CASES { encoded := string(hex.encode(transmute([]byte)test[0])) - expect( + defer delete(encoded) + testing.expectf( t, 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) { for test in CASES { decoded, ok := hex.decode(transmute([]byte)test[1]) - expect(t, ok, fmt.tprintf("decode: %q not ok", test[1])) - expect( + defer delete(decoded) + testing.expectf( + t, + ok, + "decode: %q not ok", + test[1], + ) + testing.expectf( t, 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) hex_decode_sequence :: proc(t: ^testing.T) { b, ok := hex.decode_sequence("0x23") - expect(t, ok, "decode_sequence: 0x23 not ok") - expect(t, b == '#', fmt.tprintf("decode_sequence: 0x23 -> %c (should be: %c)", b, '#')) + testing.expect(t, ok, "decode_sequence: 0x23 not ok") + testing.expectf( + t, + b == '#', + "decode_sequence: 0x23 -> %c (should be: %c)", + b, + '#', + ) b, ok = hex.decode_sequence("0X3F") - expect(t, ok, "decode_sequence: 0X3F not ok") - expect(t, b == '?', fmt.tprintf("decode_sequence: 0X3F -> %c (should be: %c)", b, '?')) + testing.expect(t, ok, "decode_sequence: 0X3F not ok") + testing.expectf( + t, + b == '?', + "decode_sequence: 0X3F -> %c (should be: %c)", + b, + '?', + ) b, ok = hex.decode_sequence("2a") - expect(t, ok, "decode_sequence: 2a not ok") - expect(t, b == '*', fmt.tprintf("decode_sequence: 2a -> %c (should be: %c)", b, '*')) + testing.expect(t, ok, "decode_sequence: 2a not ok") + testing.expectf(t, + b == '*', + "decode_sequence: 2a -> %c (should be: %c)", + b, + '*', + ) _, 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") - expect(t, !ok, "decode_sequence: 123 should be too long") -} + testing.expect(t, !ok, "decode_sequence: 123 should be too long") +} \ No newline at end of file diff --git a/tests/core/encoding/hxa/test_core_hxa.odin b/tests/core/encoding/hxa/test_core_hxa.odin index 40c3c2e23a4..31d40c8b34f 100644 --- a/tests/core/encoding/hxa/test_core_hxa.odin +++ b/tests/core/encoding/hxa/test_core_hxa.odin @@ -6,127 +6,99 @@ package test_core_hxa import "core:encoding/hxa" import "core:fmt" 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() { - t := testing.T{} - - test_read(&t) - test_write(&t) - - tc.report(&t) -} +import "core:os" @test test_read :: proc(t: ^testing.T) { - filename := tc.get_data_path(t, TEAPOT_PATH) - defer delete(filename) + data, _ := os.read_entire_file(TEAPOT_PATH) + // 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 - tc.expect(t, err == e, fmt.tprintf("%v: read_from_file(%v) -> %v != %v", #procedure, filename, err, e)) - defer hxa.file_destroy(file) + testing.expectf(t, err == e, "read_from_file(%v) -> %v != %v", TEAPOT_PATH, err, e) /* Header */ - tc.expect(t, file.magic_number == 0x417848, fmt.tprintf("%v: file.magic_number %v != %v", - #procedure, file.magic_number, 0x417848)) - tc.expect(t, file.version == 1, fmt.tprintf("%v: file.version %v != %v", - #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)) + testing.expectf(t, file.magic_number == 0x417848, "file.magic_number %v != %v", file.magic_number, 0x417848) + testing.expectf(t, file.version == 1, "file.version %v != %v", file.version, 1) + testing.expectf(t, file.internal_node_count == 1, "file.internal_node_count %v != %v", file.internal_node_count, 1) /* 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 - 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" - 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) - tc.expect(t, m_v_ok, fmt.tprintf("%v: m_v_ok %v != %v", #procedure, m_v_ok, true)) - tc.expect(t, len(m_v) == 1, fmt.tprintf("%v: len(m_v) %v != %v", #procedure, 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_ok, "m_v_ok %v != %v", m_v_ok, true) + testing.expectf(t, len(m_v) == 1, "len(m_v) %v != %v", len(m_v), 1) + testing.expectf(t, m_v[0] == 1024, "m_v[0] %v != %v", len(m_v), 1024) } { 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) - tc.expect(t, m_v_ok, fmt.tprintf("%v: m_v_ok %v != %v", #procedure, m_v_ok, true)) - tc.expect(t, len(m_v) == 1, fmt.tprintf("%v: len(m_v) %v != %v", #procedure, 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_ok, "m_v_ok %v != %v", m_v_ok, true) + testing.expectf(t, len(m_v) == 1, "len(m_v) %v != %v", len(m_v), 1) + testing.expectf(t, m_v[0] == -2054847231, "m_v[0] %v != %v", len(m_v), -2054847231) } /* Node content */ 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)) - tc.expect(t, v.edge_corner_count == 2026, fmt.tprintf("%v: v.edge_corner_count %v != %v", - #procedure, v.edge_corner_count, 2026)) - tc.expect(t, v.face_count == 517, fmt.tprintf("%v: v.face_count %v != %v", #procedure, v.face_count, 517)) + testing.expectf(t, v.vertex_count == 530, "v.vertex_count %v != %v", v.vertex_count, 530) + testing.expectf(t, v.edge_corner_count == 2026, "v.edge_corner_count %v != %v", v.edge_corner_count, 2026) + testing.expectf(t, v.face_count == 517, "v.face_count %v != %v", v.face_count, 517) /* Vertex stack */ - tc.expect(t, len(v.vertex_stack) == 1, fmt.tprintf("%v: len(v.vertex_stack) %v != %v", - #procedure, len(v.vertex_stack), 1)) + testing.expectf(t, len(v.vertex_stack) == 1, "len(v.vertex_stack) %v != %v", len(v.vertex_stack), 1) { e := "vertex" - tc.expect(t, v.vertex_stack[0].name == e, fmt.tprintf("%v: v.vertex_stack[0].name %v != %v", - #procedure, v.vertex_stack[0].name, e)) + testing.expectf(t, v.vertex_stack[0].name == e, "v.vertex_stack[0].name %v != %v", 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", - #procedure, v.vertex_stack[0].components, 3)) + testing.expectf(t, v.vertex_stack[0].components == 3, "v.vertex_stack[0].components %v != %v", v.vertex_stack[0].components, 3) /* Vertex stack data */ 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)) - tc.expect(t, len(vs_d) == 1590, fmt.tprintf("%v: len(vs_d) %v != %v", #procedure, len(vs_d), 1590)) - - tc.expect(t, vs_d[0] == 4.06266, fmt.tprintf("%v: vs_d[0] %v (%h) != %v (%h)", - #procedure, vs_d[0], vs_d[0], 4.06266, 4.06266)) - tc.expect(t, vs_d[1] == 2.83457, fmt.tprintf("%v: vs_d[1] %v (%h) != %v (%h)", - #procedure, vs_d[1], vs_d[1], 2.83457, 2.83457)) - tc.expect(t, vs_d[2] == 0hbfbc5da6a4441787, fmt.tprintf("%v: vs_d[2] %v (%h) != %v (%h)", - #procedure, vs_d[2], vs_d[2], - 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)) + testing.expectf(t, vs_d_ok, "vs_d_ok %v != %v", vs_d_ok, true) + 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) + 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) + testing.expectf(t, vs_d[2] == 0hbfbc5da6a4441787, "vs_d[2] %v (%h) != %v (%h)", vs_d[2], vs_d[2], 0hbfbc5da6a4441787, 0hbfbc5da6a4441787) + testing.expectf(t, vs_d[3] == 0h4010074fb549f948, "vs_d[3] %v (%h) != %v (%h)", vs_d[3], vs_d[3], 0h4010074fb549f948, 0h4010074fb549f948) + testing.expectf(t, vs_d[1587] == 0h400befa82e87d2c7, "vs_d[1587] %v (%h) != %v (%h)", vs_d[1587], vs_d[1587], 0h400befa82e87d2c7, 0h400befa82e87d2c7) + 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) + 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) /* Corner stack */ - tc.expect(t, len(v.corner_stack) == 1, - fmt.tprintf("%v: len(v.corner_stack) %v != %v", #procedure, len(v.corner_stack), 1)) + testing.expectf(t, len(v.corner_stack) == 1, "len(v.corner_stack) %v != %v", len(v.corner_stack), 1) { e := "reference" - tc.expect(t, v.corner_stack[0].name == e, fmt.tprintf("%v: v.corner_stack[0].name %v != %v", - #procedure, v.corner_stack[0].name, e)) + testing.expectf(t, v.corner_stack[0].name == e, "v.corner_stack[0].name %v != %v", 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", - #procedure, v.corner_stack[0].components, 1)) + testing.expectf(t, v.corner_stack[0].components == 1, "v.corner_stack[0].components %v != %v", v.corner_stack[0].components, 1) /* Corner stack data */ 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)) - tc.expect(t, len(cs_d) == 2026, fmt.tprintf("%v: len(cs_d) %v != %v", #procedure, len(cs_d), 2026)) - tc.expect(t, cs_d[0] == 6, fmt.tprintf("%v: cs_d[0] %v != %v", #procedure, 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_ok, "cs_d_ok %v != %v", cs_d_ok, true) + testing.expectf(t, len(cs_d) == 2026, "len(cs_d) %v != %v", len(cs_d), 2026) + testing.expectf(t, cs_d[0] == 6, "cs_d[0] %v != %v", cs_d[0], 6) + testing.expectf(t, cs_d[2025] == -32, "cs_d[2025] %v != %v", cs_d[2025], -32) /* Edge and face stacks (empty) */ - tc.expect(t, len(v.edge_stack) == 0, fmt.tprintf("%v: len(v.edge_stack) %v != %v", - #procedure, len(v.edge_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)) + testing.expectf(t, len(v.edge_stack) == 0, "len(v.edge_stack) %v != %v", len(v.edge_stack), 0) + testing.expectf(t, len(v.face_stack) == 0, "len(v.face_stack) %v != %v", len(v.face_stack), 0) } @test @@ -154,72 +126,72 @@ test_write :: proc(t: ^testing.T) { n, write_err := hxa.write(buf, w_file) 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)) - tc.expect(t, n == required_size, fmt.tprintf("%v: n %v != %v", #procedure, n, required_size)) + testing.expectf(t, write_err == write_e, fmt.tprintf("write_err %v != %v", write_err, write_e)) + testing.expectf(t, n == required_size, fmt.tprintf("n %v != %v", n, required_size)) file, read_err := hxa.read(buf) 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) - tc.expect(t, file.magic_number == 0x417848, fmt.tprintf("%v: file.magic_number %v != %v", - #procedure, file.magic_number, 0x417848)) - tc.expect(t, file.version == 3, fmt.tprintf("%v: file.version %v != %v", #procedure, file.version, 3)) - tc.expect(t, file.internal_node_count == 1, fmt.tprintf("%v: file.internal_node_count %v != %v", - #procedure, file.internal_node_count, 1)) + testing.expectf(t, file.magic_number == 0x417848, fmt.tprintf("file.magic_number %v != %v", + file.magic_number, 0x417848)) + testing.expectf(t, file.version == 3, fmt.tprintf("file.version %v != %v", file.version, 3)) + testing.expectf(t, file.internal_node_count == 1, fmt.tprintf("file.internal_node_count %v != %v", + file.internal_node_count, 1)) - tc.expect(t, len(file.nodes) == len(w_file.nodes), fmt.tprintf("%v: len(file.nodes) %v != %v", - #procedure, len(file.nodes), len(w_file.nodes))) + testing.expectf(t, len(file.nodes) == len(w_file.nodes), fmt.tprintf("len(file.nodes) %v != %v", + len(file.nodes), len(w_file.nodes))) m := &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^))) - 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, len(m^) == len(w_m^), fmt.tprintf("len(m^) %v != %v", len(m^), len(w_m^))) + 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) - tc.expect(t, m_v_ok, fmt.tprintf("%v: m_v_ok %v != %v", #procedure, m_v_ok, true)) - tc.expect(t, len(m_v) == len(n1_m1_value), fmt.tprintf("%v: %v != len(m_v) %v", - #procedure, len(m_v), len(n1_m1_value))) + testing.expectf(t, m_v_ok, fmt.tprintf("m_v_ok %v != %v", m_v_ok, true)) + testing.expectf(t, len(m_v) == len(n1_m1_value), fmt.tprintf("%v != len(m_v) %v", + len(m_v), len(n1_m1_value))) 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", - #procedure, i, m_v[i], n1_m1_value[i])) + testing.expectf(t, m_v[i] == n1_m1_value[i], fmt.tprintf("m_v[%d] %v != %v", + i, m_v[i], n1_m1_value[i])) } 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)) - tc.expect(t, v.type == n1_content.type, fmt.tprintf("%v: v.type %v != %v", #procedure, v.type, n1_content.type)) - tc.expect(t, len(v.resolution) == 3, fmt.tprintf("%v: len(v.resolution) %v != %v", - #procedure, 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", - #procedure, len(v.image_stack), len(n1_content.image_stack))) + testing.expectf(t, v_ok, fmt.tprintf("v_ok %v != %v", v_ok, true)) + testing.expectf(t, v.type == n1_content.type, fmt.tprintf("v.type %v != %v", v.type, n1_content.type)) + testing.expectf(t, len(v.resolution) == 3, fmt.tprintf("len(v.resolution) %v != %v", + len(v.resolution), 3)) + testing.expectf(t, len(v.image_stack) == len(n1_content.image_stack), fmt.tprintf("len(v.image_stack) %v != %v", + len(v.image_stack), len(n1_content.image_stack))) for i := 0; i < len(v.image_stack); i += 1 { - tc.expect(t, v.image_stack[i].name == n1_content.image_stack[i].name, - fmt.tprintf("%v: v.image_stack[%d].name %v != %v", - #procedure, 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, - fmt.tprintf("%v: v.image_stack[%d].components %v != %v", - #procedure, i, v.image_stack[i].components, n1_content.image_stack[i].components)) + testing.expectf(t, v.image_stack[i].name == n1_content.image_stack[i].name, + fmt.tprintf("v.image_stack[%d].name %v != %v", + i, v.image_stack[i].name, n1_content.image_stack[i].name)) + testing.expectf(t, v.image_stack[i].components == n1_content.image_stack[i].components, + fmt.tprintf("v.image_stack[%d].components %v != %v", + i, v.image_stack[i].components, n1_content.image_stack[i].components)) switch n1_t in n1_content.image_stack[i].data { 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: - 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: 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)) - tc.expect(t, len(l) == len(n1_t), fmt.tprintf("%v: len(l) %v != %v", #procedure, len(l), len(n1_t))) + testing.expectf(t, l_ok, fmt.tprintf("l_ok %v != %v", l_ok, true)) + 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 { - tc.expect(t, l[j] == n1_t[j], fmt.tprintf("%v: l[%d] %v (%h) != %v (%h)", - #procedure, j, l[j], l[j], n1_t[j], n1_t[j])) + testing.expectf(t, l[j] == n1_t[j], fmt.tprintf("l[%d] %v (%h) != %v (%h)", + j, l[j], l[j], n1_t[j], n1_t[j])) } case []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)) - tc.expect(t, len(l) == len(n1_t), fmt.tprintf("%v: len(l) %v != %v", #procedure, len(l), len(n1_t))) + testing.expectf(t, l_ok, fmt.tprintf("l_ok %v != %v", l_ok, true)) + 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 { - 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])) } } } -} +} \ No newline at end of file diff --git a/tests/core/encoding/json/test_core_json.odin b/tests/core/encoding/json/test_core_json.odin index 813d11b2c63..92c050952a0 100644 --- a/tests/core/encoding/json/test_core_json.odin +++ b/tests/core/encoding/json/test_core_json.odin @@ -2,46 +2,8 @@ package test_core_json import "core:encoding/json" import "core:testing" -import "core:fmt" -import "core:os" 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 parse_json :: proc(t: ^testing.T) { @@ -72,10 +34,9 @@ parse_json :: proc(t: ^testing.T) { } ` - _, err := json.parse(transmute([]u8)json_data) - - msg := fmt.tprintf("Expected `json.parse` to return nil, got %v", err) - expect(t, err == nil, msg) + val, err := json.parse(transmute([]u8)json_data) + json.destroy_value(val) + testing.expectf(t, err == nil, "Expected `json.parse` to return nil, got %v", err) } @test @@ -83,7 +44,7 @@ out_of_memory_in_parse_json :: proc(t: ^testing.T) { arena: virtual.Arena arena_buffer: [256]byte 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) @@ -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 - msg := fmt.tprintf("Expected `json.parse` to fail with %v, got %v", expected_error, err) - expect(t, err == json.Error.Out_Of_Memory, msg) + testing.expectf(t, err == json.Error.Out_Of_Memory, "Expected `json.parse` to fail with %v, got %v", expected_error, err) } @test @@ -134,9 +95,9 @@ marshal_json :: proc(t: ^testing.T) { b = 5, } - _, err := json.marshal(my_struct) - msg := fmt.tprintf("Expected `json.marshal` to return nil, got %v", err) - expect(t, err == nil, msg) + data, err := json.marshal(my_struct) + defer delete(data) + testing.expectf(t, err == nil, "Expected `json.marshal` to return nil, got %v", err) } PRODUCTS := ` @@ -378,17 +339,12 @@ unmarshal_json :: proc(t: ^testing.T) { err := json.unmarshal(transmute([]u8)PRODUCTS, &g, json.DEFAULT_SPECIFICATION) defer cleanup(g) - msg := fmt.tprintf("Expected `json.unmarshal` to return nil, got %v", err) - expect(t, err == nil, msg) - - 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) + testing.expectf(t, err == nil, "Expected `json.unmarshal` to return nil, got %v", err) + 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) 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 := `+ + * 😃 - /` 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 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)) - expect(t, back == input, fmt.tprintf("Expected `json.unmarshal(%q)` to return %q, got %v", string(out), input, uerr)) + defer delete(back) + 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 utf8_string_of_multibyte_characters :: proc(t: ^testing.T) { - _, err := json.parse_string(`"🐛✅"`) - msg := fmt.tprintf("Expected `json.parse` to return nil, got %v", err) - expect(t, err == nil, msg) -} + val, err := json.parse_string(`"🐛✅"`) + defer json.destroy_value(val) + testing.expectf(t, err == nil, "Expected `json.parse` to return nil, got %v", err) +} \ No newline at end of file diff --git a/tests/core/encoding/varint/test_core_varint.odin b/tests/core/encoding/varint/test_core_varint.odin index ee1798aa725..5058f30225d 100644 --- a/tests/core/encoding/varint/test_core_varint.odin +++ b/tests/core/encoding/varint/test_core_varint.odin @@ -2,110 +2,74 @@ package test_core_varint import "core:encoding/varint" import "core:testing" -import "core:fmt" -import "core:os" import "core:slice" import "core:math/rand" -TEST_count := 0 -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) - } -} +NUM_RANDOM_TESTS_PER_BYTE_SIZE :: 10_000 @(test) -test_leb128 :: proc(t: ^testing.T) { +test_uleb :: proc(t: ^testing.T) { buf: [varint.LEB128_MAX_BYTES]u8 for vector in ULEB_Vectors { 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) - expect(t, size == vector.size && val == vector.value, msg) - - msg = fmt.tprintf("Expected decoder to return error %v, got %v for vector %v", vector.error, err, vector) - expect(t, err == vector.error, msg) + 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) + testing.expectf(t, err == vector.error, "Expected decoder to return error %v, got %v for vector %v", vector.error, err, vector) if err == .None { // Try to roundtrip 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]) - expect(t, size == vector.size && slice.simple_equal(vector.encoded, buf[:size]), msg) + 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]) } } +} + +@(test) +test_ileb :: proc(t: ^testing.T) { + buf: [varint.LEB128_MAX_BYTES]u8 for vector in ILEB_Vectors { 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) - expect(t, size == vector.size && val == vector.value, msg) - - msg = fmt.tprintf("Expected decoder to return error %v, got %v", vector.error, err) - expect(t, err == vector.error, msg) + 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) + testing.expectf(t, err == vector.error, "Expected decoder to return error %v, got %v", vector.error, err) if err == .None { // Try to roundtrip 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]) - expect(t, size == vector.size && slice.simple_equal(vector.encoded, buf[:size]), msg) + 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]) } } +} + +@(test) +test_random :: proc(t: ^testing.T) { + buf: [varint.LEB128_MAX_BYTES]u8 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) - { 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) - expect(t, encode_err == .None, msg) + testing.expectf(t, encode_err == .None, "%v failed to encode as an unsigned LEB128 value, got %v", unsigned, encode_err) 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) - expect(t, decode_err == .None && decode_size == encode_size && decoded == unsigned, msg) + 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) } { 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) - expect(t, encode_err == .None, msg) + testing.expectf(t, encode_err == .None, "%v failed to encode as a signed LEB128 value, got %v", signed, encode_err) 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) - expect(t, decode_err == .None && decode_size == encode_size && decoded == signed, msg) + 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) } } } } +@(private) get_random :: proc(byte_count: uint) -> (u: u128, i: i128) { assert(byte_count >= 0 && byte_count <= size_of(u128)) diff --git a/tests/core/encoding/xml/test_core_xml.odin b/tests/core/encoding/xml/test_core_xml.odin index c6203349141..22852d1f3c1 100644 --- a/tests/core/encoding/xml/test_core_xml.odin +++ b/tests/core/encoding/xml/test_core_xml.odin @@ -2,10 +2,10 @@ package test_core_xml import "core:encoding/xml" import "core:testing" -import "core:mem" import "core:strings" import "core:io" import "core:fmt" +import "core:log" import "core:hash" Silent :: proc(pos: xml.Pos, format: string, args: ..any) {} @@ -14,9 +14,6 @@ OPTIONS :: xml.Options{ flags = { .Ignore_Unsupported, .Intern_Comments, }, expected_doctype = "", } -TEST_count := 0 -TEST_fail := 0 - TEST :: struct { filename: string, options: xml.Options, @@ -24,22 +21,14 @@ TEST :: struct { crc32: u32, } -/* - Relative to ODIN_ROOT -*/ -TEST_FILE_PATH_PREFIX :: "tests/core/assets" - -TESTS :: []TEST{ - /* - First we test that certain files parse without error. - */ +TEST_SUITE_PATH :: ODIN_ROOT + "tests/core/assets/" - { - /* - Tests UTF-8 idents and values. - Test namespaced ident. - Tests that nested partial CDATA start doesn't trip up parser. - */ +@(test) +xml_test_utf8_normal :: proc(t: ^testing.T) { + run_test(t, { + // Tests UTF-8 idents and values. + // Test namespaced ident. + // Tests that nested partial CDATA start doesn't trip up parser. filename = "XML/utf8.xml", options = { flags = { @@ -48,13 +37,14 @@ TESTS :: []TEST{ expected_doctype = "恥ずべきフクロウ", }, crc32 = 0xe9b62f03, - }, + }) +} - { - /* - Same as above. - Unbox CDATA in data tag. - */ +@(test) +xml_test_utf8_unbox_cdata :: proc(t: ^testing.T) { + run_test(t, { + // Same as above. + // Unbox CDATA in data tag. filename = "XML/utf8.xml", options = { flags = { @@ -63,13 +53,14 @@ TESTS :: []TEST{ expected_doctype = "恥ずべきフクロウ", }, crc32 = 0x9c2643ed, - }, + }) +} - { - /* - Simple Qt TS translation file. - `core:i18n` requires it to be parsed properly. - */ +@(test) +xml_test_nl_qt_ts :: proc(t: ^testing.T) { + run_test(t, { + // Simple Qt TS translation file. + // `core:i18n` requires it to be parsed properly. filename = "I18N/nl_NL-qt-ts.ts", options = { flags = { @@ -78,13 +69,14 @@ TESTS :: []TEST{ expected_doctype = "TS", }, crc32 = 0x859b7443, - }, + }) +} - { - /* - Simple XLiff 1.2 file. - `core:i18n` requires it to be parsed properly. - */ +@(test) +xml_test_xliff_1_2 :: proc(t: ^testing.T) { + run_test(t, { + // Simple XLiff 1.2 file. + // `core:i18n` requires it to be parsed properly. filename = "I18N/nl_NL-xliff-1.2.xliff", options = { flags = { @@ -93,13 +85,14 @@ TESTS :: []TEST{ expected_doctype = "xliff", }, crc32 = 0x3deaf329, - }, + }) +} - { - /* - Simple XLiff 2.0 file. - `core:i18n` requires it to be parsed properly. - */ +@(test) +xml_test_xliff_2_0 :: proc(t: ^testing.T) { + run_test(t, { + // Simple XLiff 2.0 file. + // `core:i18n` requires it to be parsed properly. filename = "I18N/nl_NL-xliff-2.0.xliff", options = { flags = { @@ -108,9 +101,12 @@ TESTS :: []TEST{ expected_doctype = "xliff", }, crc32 = 0x0c55e287, - }, + }) +} - { +@(test) +xml_test_entities :: proc(t: ^testing.T) { + run_test(t, { filename = "XML/entities.html", options = { flags = { @@ -119,9 +115,12 @@ TESTS :: []TEST{ expected_doctype = "html", }, crc32 = 0x05373317, - }, + }) +} - { +@(test) +xml_test_entities_unbox :: proc(t: ^testing.T) { + run_test(t, { filename = "XML/entities.html", options = { flags = { @@ -130,9 +129,12 @@ TESTS :: []TEST{ expected_doctype = "html", }, crc32 = 0x3b6d4a90, - }, + }) +} - { +@(test) +xml_test_entities_unbox_decode :: proc(t: ^testing.T) { + run_test(t, { filename = "XML/entities.html", options = { flags = { @@ -141,12 +143,12 @@ TESTS :: []TEST{ expected_doctype = "html", }, crc32 = 0x5be2ffdc, - }, + }) +} - /* - Then we test that certain errors are returned as expected. - */ - { +@(test) +xml_test_invalid_doctype :: proc(t: ^testing.T) { + run_test(t, { filename = "XML/utf8.xml", options = { flags = { @@ -156,12 +158,12 @@ TESTS :: []TEST{ }, err = .Invalid_DocType, crc32 = 0x49b83d0a, - }, + }) +} - /* - Parse the 9.08 MiB unicode.xml for good measure. - */ - { +@(test) +xml_test_unicode :: proc(t: ^testing.T) { + run_test(t, { filename = "XML/unicode.xml", options = { flags = { @@ -171,39 +173,37 @@ TESTS :: []TEST{ }, err = .None, crc32 = 0x0b6100ab, - }, + }) } -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] LOG:\n\t%v\n", loc, v) - } -} +@(private) +run_test :: proc(t: ^testing.T, test: TEST) { + path := strings.concatenate({TEST_SUITE_PATH, test.filename}) + defer delete(path) -test_file_path :: proc(filename: string) -> (path: string) { + doc, err := xml.load_from_file(path, test.options, Silent) + defer xml.destroy(doc) - path = fmt.tprintf("%v%v/%v", ODIN_ROOT, TEST_FILE_PATH_PREFIX, filename) - temp := transmute([]u8)path + tree_string := doc_to_string(doc) + tree_bytes := transmute([]u8)tree_string + defer delete(tree_bytes) - for r, i in path { - if r == '\\' { - temp[i] = '/' - } + crc32 := hash.crc32(tree_bytes) + + 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) } - return path } +@(private) doc_to_string :: proc(doc: ^xml.Document) -> (result: string) { /* 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) 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) } \ No newline at end of file diff --git a/tests/core/fmt/test_core_fmt.odin b/tests/core/fmt/test_core_fmt.odin index 82d009ac666..3e5839ae748 100644 --- a/tests/core/fmt/test_core_fmt.odin +++ b/tests/core/fmt/test_core_fmt.odin @@ -1,47 +1,8 @@ package test_core_fmt import "core:fmt" -import "core:os" -import "core:testing" 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_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) -} +import "core:testing" @(test) test_fmt_memory :: proc(t: ^testing.T) { @@ -75,7 +36,7 @@ test_fmt_doc_examples :: proc(t: ^testing.T) { } @(test) -test_fmt_options :: proc(t: ^testing.T) { +test_fmt_escaping_prefixes :: proc(t: ^testing.T) { // Escaping check(t, "% { } 0 { } } {", "%% {{ }} {} {{ }} }} {{", 0 ) @@ -86,7 +47,10 @@ test_fmt_options :: proc(t: ^testing.T) { check(t, "+3", "%+i", 3 ) check(t, "0b11", "%#b", 3 ) check(t, "0xA", "%#X", 10 ) +} +@(test) +test_fmt_indexing :: proc(t: ^testing.T) { // Specific index formatting 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) @@ -95,7 +59,10 @@ test_fmt_options :: proc(t: ^testing.T) { 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 1 1", "%[0]i %[0]i %[0]i", 1) +} +@(test) +test_fmt_width_precision :: proc(t: ^testing.T) { // Width check(t, "3.140", "%f", 3.14) check(t, "3.140", "%4f", 3.14) @@ -133,7 +100,10 @@ test_fmt_options :: proc(t: ^testing.T) { 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", "%5.*[1]f", 3.14, 3) +} +@(test) +test_fmt_arg_errors :: proc(t: ^testing.T) { // Error checking check(t, "%!(MISSING ARGUMENT)%!(NO VERB)", "%" ) @@ -156,7 +126,10 @@ test_fmt_options :: proc(t: ^testing.T) { check(t, "%!(BAD ARGUMENT NUMBER)%!(NO VERB)%!(EXTRA 0)", "%[1]", 0) check(t, "3.1%!(EXTRA 3.14)", "%.1f", 3.14, 3.14) +} +@(test) +test_fmt_python_syntax :: proc(t: ^testing.T) { // Python-like syntax check(t, "1 2 3", "{} {} {}", 1, 2, 3) check(t, "3 2 1", "{2} {1} {0}", 1, 2, 3) @@ -181,3 +154,9 @@ test_fmt_options :: proc(t: ^testing.T) { check(t, "%!(MISSING CLOSE BRACE)%!(EXTRA 1)", "{", 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) +} \ No newline at end of file diff --git a/tests/core/hash/test_core_hash.odin b/tests/core/hash/test_core_hash.odin index 932d2f34c19..c332383e7b5 100644 --- a/tests/core/hash/test_core_hash.odin +++ b/tests/core/hash/test_core_hash.odin @@ -2,201 +2,12 @@ package test_core_hash import "core:hash/xxhash" import "core:hash" -import "core:time" import "core:testing" -import "core:fmt" -import "core:os" import "core:math/rand" 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_benchmark_runner :: 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) { +test_xxhash_zero_fixed :: proc(t: ^testing.T) { many_zeroes := make([]u8, 16 * 1024 * 1024) defer delete(many_zeroes) @@ -204,62 +15,45 @@ test_xxhash_large :: proc(t: ^testing.T) { for i, v in ZERO_VECTORS { b := many_zeroes[:i] - fmt.printf("[test_xxhash_large] All at once. Size: %v\n", i) - xxh32 := xxhash.XXH32(b) xxh64 := xxhash.XXH64(b) xxh3_64 := xxhash.XXH3_64(b) xxh3_128 := xxhash.XXH3_128(b) - xxh32_error := fmt.tprintf("[ 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) - xxh3_64_error := fmt.tprintf("[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) - - 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) + testing.expectf(t, xxh32 == v.xxh_32, "[ XXH32(%03d) ] Expected: %08x, got: %08x", i, v.xxh_32, xxh32) + 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) + testing.expectf(t, xxh3_128 == v.xxh3_128, "[XXH3_128(%03d) ] Expected: %32x, got: %32x", i, v.xxh3_128, xxh3_128) } +} - when #config(RAND_STATE, -1) >= 0 && #config(RAND_INC, -1) >= 0 { - random_seed := rand.Rand{ - state = u64(#config(RAND_STATE, -1)), - inc = u64(#config(RAND_INC, -1)), - } - 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) - } +@(test) +test_xxhash_zero_streamed_random_updates :: proc(t: ^testing.T) { + many_zeroes := make([]u8, 16 * 1024 * 1024) + defer delete(many_zeroes) // Streamed for i, v in ZERO_VECTORS { 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() 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() 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() 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() 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 - + random_seed := rand.create(t.seed) for len(b) > 0 { update_size := min(len(b), rand.int_max(8192, &random_seed)) 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_128 := xxhash.XXH3_128_digest(xxh3_128_state) - xxh32_error := fmt.tprintf("[ 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) - xxh3_64_error := fmt.tprintf("[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) - - 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) + testing.expectf(t, xxh32 == v.xxh_32, "[ XXH32(%03d) ] Expected: %08x, got: %08x", i, v.xxh_32, xxh32) + 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) + testing.expectf(t, xxh3_128 == v.xxh3_128, "[XXH3_128(%03d) ] Expected: %32x, got: %32x", i, v.xxh3_128, xxh3_128) } } @test -test_xxhash_vectors :: proc(t: ^testing.T) { - fmt.println("Verifying against XXHASH_TEST_VECTOR_SEEDED:") - +test_xxhash_seeded :: proc(t: ^testing.T) { buf := make([]u8, 256) defer delete(buf) for seed, table in XXHASH_TEST_VECTOR_SEEDED { - fmt.printf("\tSeed: %v\n", seed) - for v, i in table { b := buf[:i] @@ -311,60 +96,48 @@ test_xxhash_vectors :: proc(t: ^testing.T) { xxh3_64 := xxhash.XXH3_64(b, seed) xxh3_128 := xxhash.XXH3_128(b, seed) - xxh32_error := fmt.tprintf("[ 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) - - xxh3_64_error := fmt.tprintf("[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) - - 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) + testing.expectf(t, xxh32 == v.xxh_32, "[ XXH32(%03d) ] Expected: %08x, got: %08x", i, v.xxh_32, xxh32) + 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) + testing.expectf(t, xxh3_128 == v.xxh3_128, "[XXH3_128(%03d) ] Expected: %32x, got: %32x", i, v.xxh3_128, xxh3_128) if len(b) > xxhash.XXH3_MIDSIZE_MAX { - fmt.printf("XXH3 - size: %v\n", len(b)) - xxh3_state, _ := xxhash.XXH3_create_state() xxhash.XXH3_64_reset_with_seed(xxh3_state, seed) xxhash.XXH3_64_update(xxh3_state, b) xxh3_64_streamed := xxhash.XXH3_64_digest(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) - expect(t, xxh3_64_streamed == v.xxh3_64, xxh3_64s_error) + testing.expectf(t, xxh3_64_streamed == v.xxh3_64, "[XXH3_64s(%03d) ] Expected: %16x, got: %16x", i, v.xxh3_64, xxh3_64_streamed) xxh3_state2, _ := xxhash.XXH3_create_state() xxhash.XXH3_128_reset_with_seed(xxh3_state2, seed) xxhash.XXH3_128_update(xxh3_state2, b) xxh3_128_streamed := xxhash.XXH3_128_digest(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) - expect(t, xxh3_128_streamed == v.xxh3_128, xxh3_128s_error) + testing.expectf(t, xxh3_128_streamed == v.xxh3_128, "[XXH3_128s(%03d) ] Expected: %32x, got: %32x", i, v.xxh3_128, xxh3_128_streamed) } } } +} - fmt.println("Verifying against XXHASH_TEST_VECTOR_SECRET:") - for secret, table in XXHASH_TEST_VECTOR_SECRET { - fmt.printf("\tSecret:\n\t\t\"%v\"\n", secret) +@test +test_xxhash_secret :: proc(t: ^testing.T) { + buf := make([]u8, 256) + defer delete(buf) + for secret, table in XXHASH_TEST_VECTOR_SECRET { secret_bytes := transmute([]u8)secret - for v, i in table { b := buf[:i] 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) - - expect(t, xxh3_128 == v.xxh3_128_secret, xxh3_128_error) + testing.expectf(t, xxh3_128 == v.xxh3_128_secret, "[XXH3_128(%03d)] Expected: %32x, got: %32x", i, v.xxh3_128_secret, xxh3_128) } } } @test test_crc64_vectors :: proc(t: ^testing.T) { - fmt.println("Verifying CRC-64:") - vectors := map[string][4]u64 { "123456789" = { 0x6c40df5f0b497347, // ECMA-182, @@ -379,23 +152,18 @@ test_crc64_vectors :: proc(t: ^testing.T) { 0xe7fcf1006b503b61, // ISO 3306, input and output inverted }, } + defer delete(vectors) for vector, expected in vectors { - fmt.println("\tVector:", vector) b := transmute([]u8)vector ecma := hash.crc64_ecma_182(b) xz := hash.crc64_xz(b) iso := hash.crc64_iso_3306(b) iso2 := hash.crc64_iso_3306_inverse(b) - ecma_error := fmt.tprintf("[ CRC-64 ECMA ] Expected: %016x. Got: %016x.", expected[0], ecma) - xz_error := fmt.tprintf("[ 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) - iso2_error := fmt.tprintf("[~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) + testing.expectf(t, ecma == expected[0], "[ CRC-64 ECMA ] Expected: %016x, got: %016x", expected[0], ecma) + testing.expectf(t, xz == expected[1], "[ CRC-64 XZ ] Expected: %016x, got: %016x", expected[1], xz) + testing.expectf(t, iso == expected[2], "[ CRC-64 ISO 3306] Expected: %016x, got: %016x", expected[2], iso) + testing.expectf(t, iso2 == expected[3], "[~CRC-64 ISO 3306] Expected: %016x, got: %016x", expected[3], iso2) } } \ No newline at end of file diff --git a/tests/core/hash/test_vectors_xxhash.odin b/tests/core/hash/test_vectors_xxhash.odin index 6a37aef301e..f72e2699ac6 100644 --- a/tests/core/hash/test_vectors_xxhash.odin +++ b/tests/core/hash/test_vectors_xxhash.odin @@ -1,6 +1,4 @@ -/* - Hash Test Vectors -*/ +// Hash Test Vectors package test_core_hash 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, }, }, -} +} \ No newline at end of file diff --git a/tests/core/image/build.bat b/tests/core/image/build.bat index 03ee6b9a529..5a07971b885 100644 --- a/tests/core/image/build.bat +++ b/tests/core/image/build.bat @@ -1,4 +1,2 @@ -@echo off -pushd .. -odin run image -popd \ No newline at end of file +@echo off +odin test . -define:ODIN_TEST_TRACK_MEMORY=true -define:ODIN_TEST_PROGRESS_WIDTH=12 -vet -strict-style diff --git a/tests/core/image/test_core_image.odin b/tests/core/image/test_core_image.odin index ae92ca617c2..7fc04ce6bf0 100644 --- a/tests/core/image/test_core_image.odin +++ b/tests/core/image/test_core_image.odin @@ -20,49 +20,14 @@ import "core:image/tga" import "core:bytes" import "core:hash" -import "core:fmt" import "core:strings" - import "core:mem" -import "core:os" import "core:time" -import "base:runtime" - -TEST_SUITE_PATH :: "assets/PNG" +TEST_SUITE_PATH :: ODIN_ROOT + "tests/core/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 -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 { file: string, tests: []struct { @@ -72,7 +37,6 @@ PNG_Test :: struct { hash: u32, }, } - Default :: image.Options{} Alpha_Add :: image.Options{.alpha_add_if_missing} 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} No_Channel_Expansion :: image.Options{.do_not_expand_channels, .return_metadata} -PNG_Dims :: struct { +PNG_Dims :: struct { width: int, height: int, channels: int, @@ -1430,81 +1394,94 @@ Expected_Text := map[string]map[string]png.Text { } @test -png_test :: proc(t: ^testing.T) { - - 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, +png_test_basic :: proc(t: ^testing.T) { + run_png_suite(t, Basic_PNG_Tests) +} - } +@test +png_test_interlaced :: proc(t: ^testing.T) { + run_png_suite(t, Interlaced_PNG_Tests) +} - for suite in PNG_Suites { - total_tests += run_png_suite(t, suite) - } +@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) +} - error := fmt.tprintf("Expected %v PNG tests, %v ran.", total_expected, total_tests) - expect(t, total_tests == total_expected, error) +@test +png_test_varied_idat :: proc(t: ^testing.T) { + run_png_suite(t, PNG_Varied_IDAT_Tests) } -run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) { +@test +png_test_zlib_levels :: proc(t: ^testing.T) { + run_png_suite(t, PNG_ZLIB_Levels_Tests) +} - context = runtime.default_context() +@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 { - 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 err: png.Error count := 0 for test in file.tests { - count += 1 - subtotal += 1 - passed := false - - track: mem.Tracking_Allocator - mem.tracking_allocator_init(&track, context.allocator) - context.allocator = mem.tracking_allocator(&track) + count += 1 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) - - expect(t, passed, error) + passed := (test.expected_error == nil && err == nil) || (test.expected_error == err) + testing.expectf(t, passed, "%v failed with %v.", test_file, err) if err == nil { // No point in running the other tests if it didn't load. pixels := bytes.buffer_to_bytes(&img.pixels) // This struct compare fails at -opt:2 if PNG_Dims is not #packed. - - 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 := PNG_Dims{img.width, img.height, img.channels, img.depth} 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 - 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) - expect(t, test.hash == png_hash, error) + png_hash := hash.crc32(pixels) + 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) 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) 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) - expect(t, qoi_save_err == nil, error) + testing.expectf(t, qoi_save_err == nil, "%v test %v QOI save failed with %v.", file.file, count, qoi_save_err) if qoi_save_err == nil { qoi_img, qoi_load_err := qoi.load(qoi_buffer.buf[:]) defer qoi.destroy(qoi_img) - error = fmt.tprintf("%v test %v QOI load failed with %v.", file.file, count, qoi_load_err) - expect(t, qoi_load_err == nil, error) + testing.expectf(t, qoi_load_err == nil, "%v test %v QOI load failed with %v.", file.file, count, qoi_load_err) 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) - expect(t, qoi_hash == png_hash, error) + 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) } } @@ -1537,19 +1511,15 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) { defer bytes.buffer_destroy(&tga_buffer) 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) - expect(t, tga_save_err == nil, error) - + testing.expectf(t, tga_save_err == nil, "%v test %v TGA save failed with %v.", file.file, count, tga_save_err) if tga_save_err == nil { tga_img, tga_load_err := tga.load(tga_buffer.buf[:]) defer tga.destroy(tga_img) - error = fmt.tprintf("%v test %v TGA load failed with %v.", file.file, count, tga_load_err) - expect(t, tga_load_err == nil, error) + testing.expectf(t, tga_load_err == nil, "%v test %v TGA load failed with %v.", file.file, count, tga_load_err) 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) - expect(t, tga_hash == png_hash, error) + 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) } } @@ -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) defer delete(pbm_buf) - error = fmt.tprintf("%v test %v PBM save failed with %v.", file.file, count, pbm_save_err) - expect(t, pbm_save_err == nil, error) + testing.expectf(t, pbm_save_err == nil, "%v test %v PBM save failed with %v.", file.file, count, pbm_save_err) if pbm_save_err == nil { // Try to load it again. pbm_img, pbm_load_err := pbm.load(pbm_buf) defer pbm.destroy(pbm_img) - error = fmt.tprintf("%v test %v PBM load failed with %v.", file.file, count, pbm_load_err) - expect(t, pbm_load_err == nil, error) + testing.expectf(t, pbm_load_err == nil, "%v test %v PBM load failed with %v.", file.file, count, pbm_load_err) if pbm_load_err == nil { pbm_hash := hash.crc32(pbm_img.pixels.buf[:]) - - 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) + 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) } } } @@ -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) defer delete(pbm_buf) - error = fmt.tprintf("%v test %v PBM save failed with %v.", file.file, count, pbm_save_err) - expect(t, pbm_save_err == nil, error) + testing.expectf(t, pbm_save_err == nil, "%v test %v PBM save failed with %v.", file.file, count, pbm_save_err) if pbm_save_err == nil { // Try to load it again. pbm_img, pbm_load_err := pbm.load(pbm_buf) defer pbm.destroy(pbm_img) - error = fmt.tprintf("%v test %v PBM load failed with %v.", file.file, count, pbm_load_err) - expect(t, pbm_load_err == nil, error) + testing.expectf(t, pbm_load_err == nil, "%v test %v PBM load failed with %v.", file.file, count, pbm_load_err) if pbm_load_err == nil { pbm_hash := hash.crc32(pbm_img.pixels.buf[:]) - - 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) + 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) } } } @@ -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) defer delete(float_pbm_buf) - error = fmt.tprintf("%v test %v save as PFM failed with %v", file.file, count, float_pbm_save_err) - expect(t, float_pbm_save_err == nil, error) + testing.expectf(t, float_pbm_save_err == nil, "%v test %v save as PFM failed with %v", file.file, count, float_pbm_save_err) if float_pbm_save_err == nil { // Load float image and compare. float_pbm_img, float_pbm_load_err := pbm.load(float_pbm_buf) defer pbm.destroy(float_pbm_img) - error = fmt.tprintf("%v test %v PFM load failed with %v", file.file, count, float_pbm_load_err) - expect(t, float_pbm_load_err == nil, error) + testing.expectf(t, float_pbm_load_err == nil, "%v test %v PFM load failed with %v", file.file, count, float_pbm_load_err) 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)) - expect(t, len(load_float) == len(orig_float), error) + 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)) // Compare floats equal := true @@ -1679,15 +1638,13 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) { break } } - error = fmt.tprintf("%v test %v PFM loaded floats to match", file.file, count) - expect(t, equal, error) + testing.expectf(t, equal, "%v test %v PFM loaded floats to match", file.file, count) } } } } if .return_metadata in test.options { - if v, ok := img.metadata.(^image.PNG_Info); ok { for c in v.chunks { #partial switch(c.header.type) { @@ -1696,8 +1653,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) { case "pp0n2c16", "pp0n6a08": gamma, gamma_ok := png.gamma(c) expected_gamma := f32(1.0) - error = fmt.tprintf("%v test %v gAMA is %v, expected %v.", file.file, count, gamma, expected_gamma) - expect(t, gamma == expected_gamma && gamma_ok, error) + testing.expectf(t, gamma == expected_gamma && gamma_ok, "%v test %v gAMA is %v, expected %v.", file.file, count, gamma, expected_gamma) } case .PLTE: 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) 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) - expect(t, expected_plte_len == plte.used && plte_ok, error) + 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) } case .sPLT: 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) 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) - expect(t, expected_splt_len == splt.used && splt_ok, error) + 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) 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) - expect(t, expected_splt_name == splt.name && splt_ok, error) + 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) 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}, 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) - expect(t, expected_chrm == chrm && chrm_ok, error) + testing.expectf(t, expected_chrm == chrm && chrm_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, chrm, expected_chrm) } case .pHYs: phys, phys_ok := png.phys(c) - phys_err := "%v test %v cHRM is %v, expected %v." switch (file.file) { case "cdfn2c08": expected_phys := png.pHYs{ppu_x = 1, ppu_y = 4, unit = .Unknown} - error = fmt.tprintf(phys_err, file.file, count, phys, expected_phys) - expect(t, expected_phys == phys && phys_ok, error) + testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, phys, expected_phys) case "cdhn2c08": expected_phys := png.pHYs{ppu_x = 4, ppu_y = 1, unit = .Unknown} - error = fmt.tprintf(phys_err, file.file, count, phys, expected_phys) - expect(t, expected_phys == phys && phys_ok, error) + testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, phys, expected_phys) case "cdsn2c08": expected_phys := png.pHYs{ppu_x = 1, ppu_y = 1, unit = .Unknown} - error = fmt.tprintf(phys_err, file.file, count, phys, expected_phys) - expect(t, expected_phys == phys && phys_ok, error) + testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, phys, expected_phys) case "cdun2c08": expected_phys := png.pHYs{ppu_x = 1000, ppu_y = 1000, unit = .Meter} - error = fmt.tprintf(phys_err, file.file, count, phys, expected_phys) - expect(t, expected_phys == phys && phys_ok, error) + testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, phys, expected_phys) } case .hIST: hist, hist_ok := png.hist(c) - hist_err := "%v test %v hIST has %v entries, expected %v." switch (file.file) { case "ch1n3p04": - error = fmt.tprintf(hist_err, file.file, count, hist.used, 15) - expect(t, hist.used == 15 && hist_ok, error) + testing.expectf(t, hist.used == 15 && hist_ok, "%v test %v hIST has %v entries, expected %v.", file.file, count, hist.used, 15) case "ch2n3p08": - error = fmt.tprintf(hist_err, file.file, count, hist.used, 256) - expect(t, hist.used == 256 && hist_ok, error) + testing.expectf(t, hist.used == 256 && hist_ok, "%v test %v hIST has %v entries, expected %v.", file.file, count, hist.used, 256) } case .tIME: png_time, png_time_ok := png.time(c) - time_err := "%v test %v tIME was %v, expected %v." expected_time: png.tIME 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 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} } - error = fmt.tprintf(time_err, file.file, count, png_time, expected_time) - expect(t, png_time == expected_time && png_time_ok, error) - - error = fmt.tprintf(time_core_err, file.file, count, core_time, expected_core) - expect(t, core_time == expected_core && core_time_ok, error) + 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) + 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) case .sBIT: sbit, sbit_ok := png.sbit(c) - sbit_err := "%v test %v sBIT was %v, expected %v." expected_sbit: [4]u8 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": expected_sbit = [4]u8{ 4, 4, 4, 0} } - error = fmt.tprintf(sbit_err, file.file, count, sbit, expected_sbit) - expect(t, sbit == expected_sbit && sbit_ok, error) + testing.expectf(t, sbit == expected_sbit && sbit_ok, "%v test %v sBIT was %v, expected %v.", file.file, count, sbit, expected_sbit) case .tEXt, .zTXt: text, text_ok := png.text(c) 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 text.keyword in Expected_Text[file.file] { 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) - expect(t, text.text == test_text && text_ok, error) + 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) } } } @@ -1842,74 +1778,59 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) { if file.file in Expected_Text { if text.keyword in Expected_Text[file.file] { 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) - expect(t, text.language == test.language && text_ok, error) - expect(t, text.keyword_localized == test.keyword_localized && 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) + 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) } } case "ctfn0g04": // international UTF-8, finnish if file.file in Expected_Text { if text.keyword in Expected_Text[file.file] { 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) - expect(t, text.text == test.text && text_ok, error) - expect(t, text.language == test.language && text_ok, error) - expect(t, text.keyword_localized == test.keyword_localized && text_ok, error) + 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) + 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) + 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) } } case "ctgn0g04": // international UTF-8, greek if file.file in Expected_Text { if text.keyword in Expected_Text[file.file] { 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) - expect(t, text.text == test.text && text_ok, error) - expect(t, text.language == test.language && text_ok, error) - expect(t, text.keyword_localized == test.keyword_localized && text_ok, error) + 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) + 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) + 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) } } case "cthn0g04": // international UTF-8, hindi if file.file in Expected_Text { if text.keyword in Expected_Text[file.file] { 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) - expect(t, text.text == test.text && text_ok, error) - expect(t, text.language == test.language && text_ok, error) - expect(t, text.keyword_localized == test.keyword_localized && text_ok, error) + 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) + 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) + 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) } } case "ctjn0g04": // international UTF-8, japanese if file.file in Expected_Text { if text.keyword in Expected_Text[file.file] { 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) - expect(t, text.text == test.text && text_ok, error) - expect(t, text.language == test.language && text_ok, error) - expect(t, text.keyword_localized == test.keyword_localized && text_ok, error) + 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) + 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) + 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) } } } case .eXIf: if file.file == "exif2c08" { // chunk with jpeg exif data 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) - error_len := fmt.tprintf("%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) + 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) + testing.expectf(t, len(exif.data) == 978 && exif_ok, "%v test %v eXIf data length '%v', expected '%v'.", file.file, len(exif.data), 978) } } } } } } - 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 -} +} \ No newline at end of file diff --git a/tests/core/math/big/build.bat b/tests/core/math/big/build.bat index ad199d775ed..54b715a4fd0 100644 --- a/tests/core/math/big/build.bat +++ b/tests/core/math/big/build.bat @@ -5,7 +5,7 @@ set TEST_ARGS=-fast-tests set TEST_ARGS=-no-random set TEST_ARGS= 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 Running core:math/big tests echo --- diff --git a/tests/core/math/linalg/glsl/test_linalg_glsl_math.odin b/tests/core/math/linalg/glsl/test_linalg_glsl_math.odin index cf91b8a9778..6d4571b24ee 100644 --- a/tests/core/math/linalg/glsl/test_linalg_glsl_math.odin +++ b/tests/core/math/linalg/glsl/test_linalg_glsl_math.odin @@ -1,24 +1,10 @@ // 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 import glsl "core:math/linalg/glsl" -import "core:fmt" import "core:math" 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_fract_f32 :: proc(t: ^testing.T) { @@ -45,7 +31,7 @@ test_fract_f32 :: proc(t: ^testing.T) { for d, i in data { assert(i == d.i) 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 { assert(i == d.i) 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) } -} +} \ No newline at end of file diff --git a/tests/core/math/noise/test_core_math_noise.odin b/tests/core/math/noise/test_core_math_noise.odin index a0360e69575..f835cf58c5e 100644 --- a/tests/core/math/noise/test_core_math_noise.odin +++ b/tests/core/math/noise/test_core_math_noise.odin @@ -2,42 +2,6 @@ package test_core_math_noise import "core:testing" 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 { seed: i64, @@ -51,6 +15,10 @@ Test_Vector :: struct { }, } +V2 :: noise.Vec2 +V3 :: noise.Vec3 +V4 :: noise.Vec4 + SEED_1 :: 2324223232 SEED_2 :: 932466901 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_3 :: V4{12090.0, 19411.0, 81950901.0, 4224219.0} -Noise_Tests := []Test_Vector{ - /* - `noise_2d` tests. - */ - {SEED_1, COORD_1.xy, 0.25010583, 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}, +@(test) +test_noise_2d :: proc(t: ^testing.T) { + test(t, {SEED_1, COORD_1.xy, 0.25010583, noise.noise_2d}) + test(t, {SEED_2, COORD_2.xy, -0.92513955, noise.noise_2d}) + test(t, {SEED_3, COORD_3.xy, 0.67327416, noise.noise_2d}) +} + +@(test) +test_noise_2d_improve_x :: proc(t: ^testing.T) { + 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}) +} + +@(test) +test_noise_3d_improve_xy :: proc(t: ^testing.T) { + test(t, {SEED_1, COORD_1.xyz, 0.14819577, noise.noise_3d_improve_xy}) + test(t, {SEED_2, COORD_2.xyz, -0.065345764, noise.noise_3d_improve_xy}) + test(t, {SEED_3, COORD_3.xyz, -0.37761918, noise.noise_3d_improve_xy}) +} + +@(test) +test_noise_3d_improve_xz :: proc(t: ^testing.T) { + test(t, {SEED_1, COORD_1.xyz, -0.50075006, noise.noise_3d_improve_xz}) + 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}) } -noise_test :: proc(t: ^testing.T) { - for test in Noise_Tests { - 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)) - } - - error := fmt.tprintf("Seed %v, Coord: %v, Expected: %3.8f. Got %3.8f", test.seed, test.coord, test.expected, output) - expect(t, test.expected == output, error) +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) } \ No newline at end of file diff --git a/tests/core/math/test_core_math.odin b/tests/core/math/test_core_math.odin index df989bff671..2a752e36684 100644 --- a/tests/core/math/test_core_math.odin +++ b/tests/core/math/test_core_math.odin @@ -1,49 +1,8 @@ // 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 -import "core:fmt" import "core:math" 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_classify_f16 :: proc(t: ^testing.T) { @@ -68,7 +27,7 @@ test_classify_f16 :: proc(t: ^testing.T) { for d, i in data { assert(i == d.i) 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) */ @@ -76,7 +35,7 @@ test_classify_f16 :: proc(t: ^testing.T) { v := transmute(f16)i r = math.classify_f16(v) 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 { assert(i == d.i) 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 { assert(i == d.i) 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 { assert(i == d.i) 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 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 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 @@ -237,16 +196,16 @@ test_trunc_f32 :: proc(t: ^testing.T) { for d, i in data { assert(i == d.i) 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 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 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 @@ -299,16 +258,16 @@ test_trunc_f64 :: proc(t: ^testing.T) { for d, i in data { assert(i == d.i) 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 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 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 @@ -352,16 +311,16 @@ test_round_f16 :: proc(t: ^testing.T) { for d, i in data { assert(i == d.i) 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 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 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 @@ -414,16 +373,16 @@ test_round_f32 :: proc(t: ^testing.T) { for d, i in data { assert(i == d.i) 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 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 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 @@ -476,16 +435,16 @@ test_round_f64 :: proc(t: ^testing.T) { for d, i in data { assert(i == d.i) 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 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 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 { 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 } veryclose :: proc(t: ^testing.T, a, b: f64, loc := #caller_location) -> bool { 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 } soclose :: proc(t: ^testing.T, a, b, e: f64, loc := #caller_location) -> bool { 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 } 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: 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 } @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 - if float64 == float64 { - tc.errorf(t, "NaN returns %.15g, expected NaN", float64) - } - float32 := f32(float64) - if float32 == float32 { - tc.errorf(t, "float32(NaN) is %.15g, expected NaN", float32) - } + equal := float64 == float64 + testing.expectf(t, !equal, "NaN returns %.15g, expected NaN", float64) } @test test_acos :: proc(t: ^testing.T) { for _, i in vf { a := vf[i] / 10 - if f := math.acos(a); !close(t, acos[i], f) { - tc.errorf(t, "math.acos(%.15g) = %.15g, want %.15g", a, f, acos[i]) - } + f := math.acos(a) + testing.expectf(t, close(t, acos[i], f), "math.acos(%.15g) = %.15g, want %.15g", a, f, acos[i]) } for _, i in vfacos_sc { - if f := math.acos(vfacos_sc[i]); !alike(t, acos_sc[i], f) { - tc.errorf(t, "math.acos(%.15g) = %.15g, want %.15g", vfacos_sc[i], f, acos_sc[i]) - } + f := math.acos(vfacos_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) { for _, i in vf { a := 1 + abs(vf[i]) - if f := math.acosh(a); !veryclose(t, acosh[i], f) { - tc.errorf(t, "math.acosh(%.15g) = %.15g, want %.15g", a, f, acosh[i]) - } + f := math.acosh(a) + testing.expectf(t, veryclose(t, acosh[i], f), "math.acosh(%.15g) = %.15g, want %.15g", a, f, acosh[i]) } for _, i in vfacosh_sc { - if f := math.acosh(vfacosh_sc[i]); !alike(t, acosh_sc[i], f) { - tc.errorf(t, "math.acosh(%.15g) = %.15g, want %.15g", vfacosh_sc[i], f, acosh_sc[i]) - } + f := math.acosh(vfacosh_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) { for _, i in vf { a := vf[i] / 10 - if f := math.asin(a); !veryclose(t, asin[i], f) { - tc.errorf(t, "math.asin(%.15g) = %.15g, want %.15g", a, f, asin[i]) - } + f := math.asin(a) + testing.expectf(t, veryclose(t, asin[i], f), "math.asin(%.15g) = %.15g, want %.15g", a, f, asin[i]) } for _, i in vfasin_sc { - if f := math.asin(vfasin_sc[i]); !alike(t, asin_sc[i], f) { - tc.errorf(t, "math.asin(%.15g) = %.15g, want %.15g", vfasin_sc[i], f, asin_sc[i]) - } + f := math.asin(vfasin_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_asinh :: proc(t: ^testing.T) { for _, i in vf { - if f := math.asinh(vf[i]); !veryclose(t, asinh[i], f) { - tc.errorf(t, "math.asinh(%.15g) = %.15g, want %.15g", vf[i], f, asinh[i]) - } + f := math.asinh(vf[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 { - if f := math.asinh(vfasinh_sc[i]); !alike(t, asinh_sc[i], f) { - tc.errorf(t, "math.asinh(%.15g) = %.15g, want %.15g", vfasinh_sc[i], f, asinh_sc[i]) - } + f := math.asinh(vfasinh_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_atan :: proc(t: ^testing.T) { for _, i in vf { - if f := math.atan(vf[i]); !veryclose(t, atan[i], f) { - tc.errorf(t, "math.atan(%.15g) = %.15g, want %.15g", vf[i], f, atan[i]) - } + f := math.atan(vf[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 { - if f := math.atan(vfatan_sc[i]); !alike(t, atan_sc[i], f) { - tc.errorf(t, "math.atan(%.15g) = %.15g, want %.15g", vfatan_sc[i], f, atan_sc[i]) - } + f := math.atan(vfatan_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) { for _, i in vf { a := vf[i] / 10 - if f := math.atanh(a); !veryclose(t, atanh[i], f) { - tc.errorf(t, "math.atanh(%.15g) = %.15g, want %.15g", a, f, atanh[i]) - } + f := math.atanh(a) + testing.expectf(t, veryclose(t, atanh[i], f), "math.atanh(%.15g) = %.15g, want %.15g", a, f, atanh[i]) } for _, i in vfatanh_sc { - if f := math.atanh(vfatanh_sc[i]); !alike(t, atanh_sc[i], f) { - tc.errorf(t, "math.atanh(%.15g) = %.15g, want %.15g", vfatanh_sc[i], f, atanh_sc[i]) - } + f := math.atanh(vfatanh_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_atan2 :: proc(t: ^testing.T) { for _, i in vf { - if f := math.atan2(10, vf[i]); !veryclose(t, atan2[i], f) { - tc.errorf(t, "math.atan2(10, %.15g) = %.15g, want %.15g", vf[i], f, atan2[i]) - } + f := math.atan2(10, vf[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 { - if f := math.atan2(vfatan2_sc[i][0], vfatan2_sc[i][1]); !alike(t, atan2_sc[i], f) { - tc.errorf(t, "math.atan2(%.15g, %.15g) = %.15g, want %.15g", vfatan2_sc[i][0], vfatan2_sc[i][1], f, atan2_sc[i]) - } + f := math.atan2(vfatan2_sc[i][0], vfatan2_sc[i][1]) + 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_cos :: proc(t: ^testing.T) { for _, i in vf { - if f := math.cos(vf[i]); !veryclose(t, cos[i], f) { - tc.errorf(t, "math.cos(%.15g) = %.15g, want %.15g", vf[i], f, cos[i]) - } + f := math.cos(vf[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 { - if f := math.cos(vfcos_sc[i]); !alike(t, cos_sc[i], f) { - tc.errorf(t, "math.cos(%.15g) = %.15g, want %.15g", vfcos_sc[i], f, cos_sc[i]) - } + f := math.cos(vfcos_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_cosh :: proc(t: ^testing.T) { for _, i in vf { - if f := math.cosh(vf[i]); !close(t, cosh[i], f) { - tc.errorf(t, "math.cosh(%.15g) = %.15g, want %.15g", vf[i], f, cosh[i]) - } + f := math.cosh(vf[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 { - if f := math.cosh(vfcosh_sc[i]); !alike(t, cosh_sc[i], f) { - tc.errorf(t, "math.cosh(%.15g) = %.15g, want %.15g", vfcosh_sc[i], f, cosh_sc[i]) - } + f := math.cosh(vfcosh_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_sin :: proc(t: ^testing.T) { for _, i in vf { - if f := math.sin(vf[i]); !veryclose(t, sin[i], f) { - tc.errorf(t, "math.sin(%.15g) = %.15g, want %.15g", vf[i], f, sin[i]) - } + f := math.sin(vf[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 { - if f := math.sin(vfsin_sc[i]); !alike(t, sin_sc[i], f) { - tc.errorf(t, "math.sin(%.15g) = %.15g, want %.15g", vfsin_sc[i], f, sin_sc[i]) - } + f := math.sin(vfsin_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_sinh :: proc(t: ^testing.T) { for _, i in vf { - if f := math.sinh(vf[i]); !close(t, sinh[i], f) { - tc.errorf(t, "math.sinh(%.15g) = %.15g, want %.15g", vf[i], f, sinh[i]) - } + f := math.sinh(vf[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 { - if f := math.sinh(vfsinh_sc[i]); !alike(t, sinh_sc[i], f) { - tc.errorf(t, "math.sinh(%.15g) = %.15g, want %.15g", vfsinh_sc[i], f, sinh_sc[i]) - } + f := math.sinh(vfsinh_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) { for _, i in vf { a := abs(vf[i]) - if f := math.sqrt(a); !veryclose(t, sqrt[i], f) { - tc.errorf(t, "math.sqrt(%.15g) = %.15g, want %.15g", a, f, sqrt[i]) - } + f := math.sqrt(a) + testing.expectf(t, veryclose(t, sqrt[i], f), "math.sqrt(%.15g) = %.15g, want %.15g", a, f, sqrt[i]) } } @test test_tan :: proc(t: ^testing.T) { for _, i in vf { - if f := math.tan(vf[i]); !veryclose(t, tan[i], f) { - tc.errorf(t, "math.tan(%.15g) = %.15g, want %.15g", vf[i], f, tan[i]) - } + f := math.tan(vf[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 for _, i in vfsin_sc { - if f := math.tan(vfsin_sc[i]); !alike(t, sin_sc[i], f) { - tc.errorf(t, "math.tan(%.15g) = %.15g, want %.15g", vfsin_sc[i], f, sin_sc[i]) - } + f := math.tan(vfsin_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_tanh :: proc(t: ^testing.T) { for _, i in vf { - if f := math.tanh(vf[i]); !veryclose(t, tanh[i], f) { - tc.errorf(t, "math.tanh(%.15g) = %.15g, want %.15g", vf[i], f, tanh[i]) - } + f := math.tanh(vf[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 { - if f := math.tanh(vftanh_sc[i]); !alike(t, tanh_sc[i], f) { - tc.errorf(t, "math.tanh(%.15g) = %.15g, want %.15g", vftanh_sc[i], f, tanh_sc[i]) - } + f := math.tanh(vftanh_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 { f1 := cosLarge[i] f2 := math.cos(vf[i] + large) - if !close(t, f1, f2) { - tc.errorf(t, "math.cos(%.15g) = %.15g, want %.15g", vf[i]+large, f2, f1) - } + testing.expectf(t, close(t, f1, f2), "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 { f1 := sinLarge[i] f2 := math.sin(vf[i] + large) - if !close(t, f1, f2) { - tc.errorf(t, "math.sin(%.15g) = %.15g, want %.15g", vf[i]+large, f2, f1) - } + testing.expectf(t, close(t, f1, f2), "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 { f1 := tanLarge[i] f2 := math.tan(vf[i] + large) - if !close(t, f1, f2) { - tc.errorf(t, "math.tan(%.15g) = %.15g, want %.15g", vf[i]+large, f2, f1) - } + testing.expectf(t, close(t, f1, f2), "math.tan(%.15g) = %.15g, want %.15g", vf[i]+large, f2, f1) } } \ No newline at end of file diff --git a/tests/core/net/test_core_net.odin b/tests/core/net/test_core_net.odin index 9df03414cb4..3cbb7fa34a7 100644 --- a/tests/core/net/test_core_net.odin +++ b/tests/core/net/test_core_net.odin @@ -11,71 +11,12 @@ package test_core_net import "core:testing" -import "core:mem" -import "core:fmt" import "core:net" import "core:strconv" import "core:sync" import "core:time" import "core:thread" -import "core:os" - -_, _ :: 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) - } -} +import "core:fmt" @test address_parsing_test :: proc(t: ^testing.T) { @@ -89,127 +30,66 @@ address_parsing_test :: proc(t: ^testing.T) { } valid := len(vector.binstr) > 0 - - fmt.printf("%v %v\n", kind, vector.input) - - msg := "-set a proper message-" switch vector.family { 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 + 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) - parsed_ok := any_addr != nil - parsed: net.IP4_Address - - /* - Ensure that `parse_address` doesn't parse IPv4 addresses into IPv6 addreses by mistake. - */ + // Ensure that `parse_address` doesn't parse IPv4 addresses into IPv6 addreses by mistake. switch addr in any_addr { case net.IP4_Address: parsed = addr case net.IP6_Address: parsed_ok = false - msg = fmt.tprintf("parse_address mistook %v as IPv6 address %04x", vector.input, addr) - expect(t, false, msg) + testing.expectf(t, false, "parse_address mistook %v as IPv6 address %04x", vector.input, addr) } 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 { - 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 { actual_binary := address_to_binstr(parsed) - msg = fmt.tprintf("parse_ip4_address parsed %v into %v, expected %v", vector.input, actual_binary, vector.binstr) - expect(t, actual_binary == vector.binstr, msg) + testing.expectf(t, actual_binary == vector.binstr, "parse_ip4_address parsed %v into %v, expected %v", vector.input, actual_binary, vector.binstr) - /* - 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 { stringified := net.address_to_string(parsed) - msg = fmt.tprintf("address_to_string turned %v into %v, expected %v", parsed, stringified, vector.output) - expect(t, stringified == vector.output, msg) + testing.expectf(t, stringified == vector.output, "address_to_string turned %v into %v, expected %v", parsed, stringified, vector.output) } } case .IP6: - /* - Do we parse the address properly? - */ + // Do we parse the address properly? parsed, parsed_ok := net.parse_ip6_address(vector.input) 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 { - 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 { actual_binary := address_to_binstr(parsed) - msg = fmt.tprintf("parse_ip6_address parsed %v into %v, expected %v", vector.input, actual_binary, vector.binstr) - expect(t, actual_binary == vector.binstr, msg) + testing.expectf(t, actual_binary == vector.binstr, "parse_ip6_address parsed %v into %v, expected %v", vector.input, actual_binary, vector.binstr) - /* - 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 { stringified := net.address_to_string(parsed) - msg = fmt.tprintf("address_to_string turned %v into %v, expected %v", parsed, stringified, vector.output) - expect(t, stringified == vector.output, msg) + testing.expectf(t, stringified == vector.output, "address_to_string turned %v into %v, expected %v", parsed, stringified, vector.output) } } } } } -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 { IP4, // Decimal IPv4 IP4_Alt, // Non-decimal address @@ -223,10 +103,7 @@ IP_Address_Parsing_Test_Vector :: struct { // Input address to try and parse. 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, // 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", "", ""}, } -tcp_tests :: proc(t: ^testing.T) { - fmt.println("Testing two servers trying to bind to the same endpoint...") - two_servers_binding_same_endpoint(t) - 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, -} +ENDPOINT_TWO_SERVERS := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 9991} +ENDPOINT_CLOSED_PORT := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 9992} +ENDPOINT_SERVER_SENDS := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 9993} @(test) 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) - skt2, err2 := net.listen_tcp(ENDPOINT) + skt2, err2 := net.listen_tcp(ENDPOINT_TWO_SERVERS) defer net.close(skt2) - 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, err1 == nil, "expected first server binding to endpoint to do so without error") + testing.expect(t, err2 == net.Bind_Error.Address_In_Use, "expected second server to bind to an endpoint to return .Address_In_Use") } @(test) 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) - 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) client_sends_server_data :: proc(t: ^testing.T) { CONTENT: string: "Hellope!" @@ -390,8 +259,8 @@ client_sends_server_data :: proc(t: ^testing.T) { defer sync.wait_group_done(r.wg) - if r.skt, r.err = net.dial_tcp(ENDPOINT); r.err != nil { - log(r.t, r.err) + if r.skt, r.err = net.dial_tcp(ENDPOINT_SERVER_SENDS); r.err != nil { + testing.expectf(r.t, false, "[tcp_client:dial_tcp] %v", r.err) return } @@ -405,19 +274,17 @@ client_sends_server_data :: proc(t: ^testing.T) { defer sync.wait_group_done(r.wg) - log(r.t, "tcp_server listen") - if r.skt, r.err = net.listen_tcp(ENDPOINT); r.err != nil { + if r.skt, r.err = net.listen_tcp(ENDPOINT_SERVER_SENDS); r.err != nil { sync.wait_group_done(r.wg) - log(r.t, r.err) + testing.expectf(r.t, false, "[tcp_server:listen_tcp] %v", r.err) return } sync.wait_group_done(r.wg) - log(r.t, "tcp_server accept") client: net.TCP_Socket 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 } defer net.close(client) @@ -437,10 +304,7 @@ client_sends_server_data :: proc(t: ^testing.T) { thread_data[0].wg = &wg 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) - log(t, "starting up client") - sync.wait_group_add(&wg, 2) thread_data[1].t = t @@ -454,20 +318,15 @@ client_sends_server_data :: proc(t: ^testing.T) { net.close(thread_data[1].skt) thread.destroy(thread_data[1].tid) } - - log(t, "waiting for threads to finish") sync.wait_group_wait(&wg) - log(t, "threads finished") 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) - expect(t, okay, msg) + testing.expectf(t, okay, "Expected client and server to return `nil`, got %v and %v", thread_data[0].err, thread_data[1].err) received := string(thread_data[0].data[:thread_data[0].length]) okay = received == CONTENT - msg = fmt.tprintf("Expected client to send \"{}\", got \"{}\"", CONTENT, received) - expect(t, okay, msg) + testing.expectf(t, okay, "Expected client to send \"{}\", got \"{}\"", CONTENT, received) } URL_Test :: struct { @@ -559,22 +418,15 @@ split_url_test :: proc(t: ^testing.T) { delete(test.queries) } - msg := fmt.tprintf("Expected `net.split_url` to return %s, got %s", test.scheme, scheme) - expect(t, scheme == test.scheme, msg) - msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", test.host, host) - expect(t, host == test.host, msg) - 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) + testing.expectf(t, scheme == test.scheme, "Expected `net.split_url` to return %s, got %s", test.scheme, scheme) + testing.expectf(t, host == test.host, "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) + testing.expectf(t, len(queries) == len(test.queries), "Expected `net.split_url` to return %d queries, got %d queries", len(test.queries), len(queries)) for k, v in queries { expected := test.queries[k] - msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", expected, v) - expect(t, v == expected, msg) + testing.expectf(t, v == expected, "Expected `net.split_url` to return %s, got %s", expected, v) } - msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", test.fragment, fragment) - expect(t, fragment == test.fragment, msg) - + testing.expectf(t, fragment == test.fragment, "Expected `net.split_url` to return %s, got %s", test.fragment, fragment) } } @@ -659,7 +511,45 @@ join_url_test :: proc(t: ^testing.T) { for test_url in test.url { pass |= url == test_url } - msg := fmt.tprintf("Expected `net.join_url` to return one of %s, got %s", test.url, url) - expect(t, pass, msg) + testing.expectf(t, pass, "Expected `net.join_url` to return one of %s, got %s", test.url, url) + } +} + +@(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") +} \ No newline at end of file diff --git a/tests/core/odin/test_parser.odin b/tests/core/odin/test_parser.odin index 821b7a53c9f..772ae598219 100644 --- a/tests/core/odin/test_parser.odin +++ b/tests/core/odin/test_parser.odin @@ -1,58 +1,29 @@ package test_core_odin_parser -import "core:fmt" import "core:odin/ast" import "core:odin/parser" -import "core:os" +import "base:runtime" 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_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 { - 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_parse_bitfield :: proc(t: ^testing.T) { + context.allocator = context.temp_allocator + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + file := ast.File{ fullpath = "test.odin", src = ` @@ -78,5 +49,5 @@ Foo :: bit_field uint { p := parser.default_parser() ok := parser.parse_file(&p, &file) - expect(t, ok == true, "bad parse") -} + testing.expect(t, ok, "bad parse") +} \ No newline at end of file diff --git a/tests/core/path/filepath/test_core_filepath.odin b/tests/core/path/filepath/test_core_filepath.odin index 4c70e5f2882..94b9329bb8a 100644 --- a/tests/core/path/filepath/test_core_filepath.odin +++ b/tests/core/path/filepath/test_core_filepath.odin @@ -1,26 +1,19 @@ // 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 import "core:fmt" import "core:path/filepath" import "core:testing" -import tc "tests:common" - -main :: proc() { - t := testing.T{} +@(test) +test_split_list :: proc(t: ^testing.T) { when ODIN_OS == .Windows { - test_split_list_windows(&t) + test_split_list_windows(t) } else { - test_split_list_unix(&t) + test_split_list_unix(t) } - - tc.report(&t) } -@test test_split_list_windows :: proc(t: ^testing.T) { Datum :: struct { i: int, @@ -41,12 +34,12 @@ test_split_list_windows :: proc(t: ^testing.T) { for d, i 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) - defer delete(r) - tc.expect(t, len(r) == len(d.e), fmt.tprintf("i:%d %s(%s) len(r) %d != len(d.e) %d", + defer delete_split(r) + 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))) if len(r) == len(d.e) { 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])) } } @@ -55,47 +48,43 @@ test_split_list_windows :: proc(t: ^testing.T) { { 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" r := filepath.split_list(v) - defer delete(r) - tc.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r))) + defer delete_split(r) + testing.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r))) 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) { Datum :: struct { - i: int, v: string, e: [3]string, } @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 - { 1, "a::b", [3]string{"a", "", "b"} }, - { 2, "a:b:", [3]string{"a", "b", ""} }, - { 3, ":a:b", [3]string{"", "a", "b"} }, - { 4, "::", [3]string{"", "", ""} }, - { 5, "\"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", [3]string{"a", "", "b"} }, + { "a:b:", [3]string{"a", "b", ""} }, + { ":a:b", [3]string{"", "a", "b"} }, + { "::", [3]string{"", "", ""} }, + { "\"a:b\"c:d:\"f\"", [3]string{"a:bc", "d", "f"} }, + { "\"a:b:c\":d\":e\":f", [3]string{"a:b:c", "d:e", "f"} }, } - for d, i in data { - assert(i == d.i, fmt.tprintf("wrong data index: i %d != d.i %d\n", i, d.i)) + for d in data { r := filepath.split_list(d.v) - defer delete(r) - tc.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))) + defer delete_split(r) + testing.expectf(t, len(r) == len(d.e), "%s len(r) %d != len(d.e) %d", d.v, len(r), len(d.e)) if len(r) == len(d.e) { for _, j in r { - tc.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])) + testing.expectf(t, r[j] == d.e[j], "%v -> %v[%d] != %v", d.v, r[j], j, d.e[j]) } } } @@ -103,15 +92,23 @@ test_split_list_unix :: proc(t: ^testing.T) { { 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" r := filepath.split_list(v) - defer delete(r) - tc.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r))) + defer delete_split(r) + testing.expectf(t, len(r) == 1, "'%s' len(r) %d != 1", v, len(r)) 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) +} \ No newline at end of file diff --git a/tests/core/reflect/test_core_reflect.odin b/tests/core/reflect/test_core_reflect.odin index a3a66f968af..7d239468887 100644 --- a/tests/core/reflect/test_core_reflect.odin +++ b/tests/core/reflect/test_core_reflect.odin @@ -1,21 +1,8 @@ // 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 -import "core:fmt" import "core:reflect" 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_as_u64 :: proc(t: ^testing.T) { @@ -31,9 +18,8 @@ test_as_u64 :: proc(t: ^testing.T) { for d, i in data { assert(i == d.i) r, valid := reflect.as_u64(d.v) - tc.expect(t, valid, fmt.tprintf("i:%d %s(i8 %v) !valid\n", i, #procedure, d.v)) - tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i8 %v) -> %v (0x%X) != %v (0x%X)\n", - i, #procedure, d.v, r, r, d.e, d.e)) + testing.expectf(t, valid, "i8 %v !valid", d.v) + testing.expectf(t, r == d.e, "i8 %v -> %v (0x%X) != %v (0x%X)", d.v, r, r, d.e, d.e) } } { @@ -48,9 +34,8 @@ test_as_u64 :: proc(t: ^testing.T) { for d, i in data { assert(i == d.i) r, valid := reflect.as_u64(d.v) - tc.expect(t, valid, fmt.tprintf("i:%d %s(i16 %v) !valid\n", i, #procedure, d.v)) - tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i16 %v) -> %v (0x%X) != %v (0x%X)\n", - i, #procedure, d.v, r, r, d.e, d.e)) + testing.expectf(t, valid, "i16 %v !valid", d.v) + testing.expectf(t, r == d.e, "i16 %v -> %v (0x%X) != %v (0x%X)", d.v, r, r, d.e, d.e) } } { @@ -65,9 +50,8 @@ test_as_u64 :: proc(t: ^testing.T) { for d, i in data { assert(i == d.i) r, valid := reflect.as_u64(d.v) - tc.expect(t, valid, fmt.tprintf("i:%d %s(i32 %v) !valid\n", i, #procedure, d.v)) - tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i32 %v) -> %v (0x%X) != %v (0x%X)\n", - i, #procedure, d.v, r, r, d.e, d.e)) + testing.expectf(t, valid, "i32 %v !valid", d.v) + testing.expectf(t, r == d.e, "i32 %v -> %v (0x%X) != %v (0x%X)", d.v, r, r, d.e, d.e) } } { @@ -82,9 +66,8 @@ test_as_u64 :: proc(t: ^testing.T) { for d, i in data { assert(i == d.i) r, valid := reflect.as_u64(d.v) - tc.expect(t, valid, fmt.tprintf("i:%d %s(i64 %v) !valid\n", i, #procedure, d.v)) - tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i64 %v) -> %v (0x%X) != %v (0x%X)\n", - i, #procedure, d.v, r, r, d.e, d.e)) + testing.expectf(t, valid, "i64 %v !valid", d.v) + testing.expectf(t, r == d.e, "i64 %v -> %v (0x%X) != %v (0x%X)", d.v, r, r, d.e, d.e) } } { @@ -102,9 +85,8 @@ test_as_u64 :: proc(t: ^testing.T) { for d, i in data { assert(i == d.i) r, valid := reflect.as_u64(d.v) - tc.expect(t, valid, fmt.tprintf("i:%d %s(i128 %v) !valid\n", i, #procedure, d.v)) - tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i128 %v) -> %v (0x%X) != %v (0x%X)\n", - i, #procedure, d.v, r, r, d.e, d.e)) + testing.expectf(t, valid, "i128 %v !valid", d.v) + testing.expectf(t, r == d.e, "i128 %v -> %v (0x%X) != %v (0x%X)", d.v, r, r, d.e, d.e) } } { @@ -118,8 +100,8 @@ test_as_u64 :: proc(t: ^testing.T) { for d, i in data { assert(i == d.i) r, valid := reflect.as_u64(d.v) - tc.expect(t, valid, fmt.tprintf("i:%d %s(f16 %v) !valid\n", i, #procedure, 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, valid, "f16 %v !valid", d.v) + 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 { assert(i == d.i) r, valid := reflect.as_u64(d.v) - tc.expect(t, valid, fmt.tprintf("i:%d %s(f32 %v) !valid\n", i, #procedure, 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, valid, "f32 %v !valid", d.v) + 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 { assert(i == d.i) r, valid := reflect.as_u64(d.v) - tc.expect(t, valid, fmt.tprintf("i:%d %s(f64 %v) !valid\n", i, #procedure, 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, valid, "f64 %v !valid", d.v) + 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 { assert(i == d.i) r, valid := reflect.as_f64(d.v) - tc.expect(t, valid, fmt.tprintf("i:%d %s(i8 %v) !valid\n", i, #procedure, 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, valid, "i8 %v !valid", d.v) + 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 { assert(i == d.i) r, valid := reflect.as_f64(d.v) - tc.expect(t, valid, fmt.tprintf("i:%d %s(i16 %v) !valid\n", i, #procedure, 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, valid, "i16 %v !valid", d.v) + 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 { assert(i == d.i) r, valid := reflect.as_f64(d.v) - tc.expect(t, valid, fmt.tprintf("i:%d %s(i32 %v) !valid\n", i, #procedure, 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, valid, "i32 %v !valid", d.v) + 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 { assert(i == d.i) r, valid := reflect.as_f64(d.v) - tc.expect(t, valid, fmt.tprintf("i:%d %s(i64 %v) !valid\n", i, #procedure, 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, valid, "i64 %v !valid", d.v) + 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 { assert(i == d.i) r, valid := reflect.as_f64(d.v) - tc.expect(t, valid, fmt.tprintf("i:%d %s(i128 %v) !valid\n", i, #procedure, d.v)) - tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i128 %v) -> %v (%H) != %v (%H)\n", - i, #procedure, d.v, r, r, d.e, d.e)) + testing.expectf(t, valid, "i128 %v !valid", d.v) + testing.expectf(t, r == d.e, "i128 %v -> %v (%H) != %v (%H)", d.v, r, r, d.e, d.e) } } { @@ -247,9 +228,8 @@ test_as_f64 :: proc(t: ^testing.T) { for d, i in data { assert(i == d.i) r, valid := reflect.as_f64(d.v) - tc.expect(t, valid, fmt.tprintf("i:%d %s(f16 %v) !valid\n", i, #procedure, d.v)) - tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f16 %v (%H)) -> %v (%H) != %v (%H)\n", - i, #procedure, d.v, d.v, r, r, d.e, d.e)) + testing.expectf(t, valid, "f16 %v !valid", d.v) + testing.expectf(t, r == d.e, "f16 %v (%H) -> %v (%H) != %v (%H)", 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 { assert(i == d.i) r, valid := reflect.as_f64(d.v) - tc.expect(t, valid, fmt.tprintf("i:%d %s(f32 %v) !valid\n", i, #procedure, d.v)) - tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f32 %v (%H)) -> %v (%H) != %v (%H)\n", - i, #procedure, d.v, d.v, r, r, d.e, d.e)) + testing.expectf(t, valid, "f32 %v !valid", d.v) + testing.expectf(t, r == d.e, "f32 %v (%H) -> %v (%H) != %v (%H)", 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 { assert(i == d.i) r, valid := reflect.as_f64(d.v) - tc.expect(t, valid, fmt.tprintf("i:%d %s(f64 %v) !valid\n", i, #procedure, 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, valid, "f64 %v !valid", d.v) + testing.expectf(t, r == d.e, "f64 %v -> %v != %v", d.v, r, d.e) } } -} +} \ No newline at end of file diff --git a/tests/core/runtime/test_core_runtime.odin b/tests/core/runtime/test_core_runtime.odin index 786cf003a8a..008146dcf5e 100644 --- a/tests/core/runtime/test_core_runtime.odin +++ b/tests/core/runtime/test_core_runtime.odin @@ -1,43 +1,10 @@ package test_core_runtime -import "core:fmt" import "base:intrinsics" import "core:mem" -import "core:os" -import "core:reflect" import "base:runtime" 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 // is handled correctly. @(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) _, 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. @@ -58,7 +25,7 @@ test_temp_allocator_big_alloc_and_alignment :: proc(t: ^testing.T) { mappy: map[[8]int]int err := reserve(&mappy, 50000) - expect_value(t, err, nil) + testing.expect(t, err == nil) } @(test) @@ -67,6 +34,6 @@ test_temp_allocator_returns_correct_size :: proc(t: ^testing.T) { context.allocator = runtime.arena_allocator(&arena) bytes, err := mem.alloc_bytes(10, 16) - expect_value(t, err, nil) - expect_value(t, len(bytes), 10) -} + testing.expect(t, err == nil) + testing.expect(t, len(bytes) == 10) +} \ No newline at end of file diff --git a/tests/core/slice/test_core_slice.odin b/tests/core/slice/test_core_slice.odin index 06329ddda14..4a464503fb0 100644 --- a/tests/core/slice/test_core_slice.odin +++ b/tests/core/slice/test_core_slice.odin @@ -1,56 +1,16 @@ package test_core_slice import "core:slice" -import "core:strings" import "core:testing" -import "core:fmt" -import "core:os" 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_sort_with_indices :: proc(t: ^testing.T) { - seed := rand.uint64() - fmt.printf("Random seed: %v\n", seed) - // Test sizes are all prime. test_sizes :: []int{7, 13, 347, 1031, 10111, 100003} for test_size in test_sizes { - fmt.printf("Sorting %v random u64 values along with index.\n", test_size) - - r := rand.create(seed) + r := rand.create(t.seed) vals := make([]u64, test_size) r_idx := make([]int, test_size) // Reverse index @@ -61,7 +21,7 @@ test_sort_with_indices :: proc(t: ^testing.T) { // Set up test values for _, i in vals { - vals[i] = rand.uint64(&r) + vals[i] = rand.uint64(&r) } // Sort @@ -69,7 +29,7 @@ test_sort_with_indices :: proc(t: ^testing.T) { defer delete(f_idx) // Verify sorted test values - rand.init(&r, seed) + rand.init(&r, t.seed) for v, i in f_idx { r_idx[v] = i @@ -79,14 +39,14 @@ test_sort_with_indices :: proc(t: ^testing.T) { for v, i in vals { if i > 0 { 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 { break } } 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 { break } @@ -97,16 +57,11 @@ test_sort_with_indices :: proc(t: ^testing.T) { @test 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 :: []int{7, 13, 347, 1031, 10111, 100003} for test_size in test_sizes { - fmt.printf("Sorting %v random u64 values along with index.\n", test_size) - - r := rand.create(seed) + r := rand.create(t.seed) vals := make([]u64, test_size) r_idx := make([]int, test_size) // Reverse index @@ -117,7 +72,7 @@ test_sort_by_indices :: proc(t: ^testing.T) { // Set up test values for _, i in vals { - vals[i] = rand.uint64(&r) + vals[i] = rand.uint64(&r) } // Sort @@ -125,7 +80,7 @@ test_sort_by_indices :: proc(t: ^testing.T) { defer delete(f_idx) // Verify sorted test values - rand.init(&r, seed) + rand.init(&r, t.seed) { indices := make([]int, test_size) @@ -138,7 +93,7 @@ test_sort_by_indices :: proc(t: ^testing.T) { defer delete(sorted_indices) for v, i in sorted_indices { 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 { break } @@ -154,7 +109,7 @@ test_sort_by_indices :: proc(t: ^testing.T) { slice.sort_by_indices_overwrite(indices, f_idx) for v, i in indices { 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 { break } @@ -174,7 +129,7 @@ test_sort_by_indices :: proc(t: ^testing.T) { slice.sort_by_indices(indices, swap, f_idx) for v, i in swap { 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 { break } @@ -185,61 +140,48 @@ test_sort_by_indices :: proc(t: ^testing.T) { @test 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 found: bool s := []i32{0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55} - index, found = test_search(t, &builder, s, 13) - expect(t, index == 9, "Expected index to be 9.") - expect(t, found == true, "Expected found to be true.") + index, found = slice.binary_search(s, 13) + testing.expect(t, index == 9, "Expected index to be 9") + testing.expect(t, found == true, "Expected found to be true") - index, found = test_search(t, &builder, s, 4) - expect(t, index == 7, "Expected index to be 7.") - expect(t, found == false, "Expected found to be false.") + index, found = slice.binary_search(s, 4) + testing.expect(t, index == 7, "Expected index to be 7.") + testing.expect(t, found == false, "Expected found to be false.") - index, found = test_search(t, &builder, s, 100) - expect(t, index == 13, "Expected index to be 13.") - expect(t, found == false, "Expected found to be false.") + index, found = slice.binary_search(s, 100) + testing.expect(t, index == 13, "Expected index to be 13.") + testing.expect(t, found == false, "Expected found to be false.") - index, found = test_search(t, &builder, s, 1) - expect(t, index >= 1 && index <= 4, "Expected index to be 1, 2, 3, or 4.") - expect(t, found == true, "Expected found to be true.") + index, found = slice.binary_search(s, 1) + testing.expect(t, index >= 1 && index <= 4, "Expected index to be 1, 2, 3, or 4.") + testing.expect(t, found == true, "Expected found to be true.") - index, found = test_search(t, &builder, s, -1) - expect(t, index == 0, "Expected index to be 0.") - expect(t, found == false, "Expected found to be false.") + index, found = slice.binary_search(s, -1) + testing.expect(t, index == 0, "Expected index to be 0.") + testing.expect(t, found == false, "Expected found to be false.") a := []i32{} - index, found = test_search(t, &builder, a, 13) - expect(t, index == 0, "Expected index to be 0.") - expect(t, found == false, "Expected found to be false.") + index, found = slice.binary_search(a, 13) + testing.expect(t, index == 0, "Expected index to be 0.") + testing.expect(t, found == false, "Expected found to be false.") b := []i32{1} - index, found = test_search(t, &builder, b, 13) - expect(t, index == 1, "Expected index to be 1.") - expect(t, found == false, "Expected found to be false.") + index, found = slice.binary_search(b, 13) + testing.expect(t, index == 1, "Expected index to be 1.") + testing.expect(t, found == false, "Expected found to be false.") - index, found = test_search(t, &builder, b, 1) - expect(t, index == 0, "Expected index to be 0.") - expect(t, found == true, "Expected found to be true.") + index, found = slice.binary_search(b, 1) + testing.expect(t, index == 0, "Expected index to be 0.") + testing.expect(t, found == true, "Expected found to be true.") - index, found = test_search(t, &builder, b, 0) - expect(t, index == 0, "Expected index to be 0.") - expect(t, found == false, "Expected found to be false.") -} + index, found = slice.binary_search(b, 0) + testing.expect(t, index == 0, "Expected index to be 0.") + testing.expect(t, found == false, "Expected found to be false.") +} \ No newline at end of file diff --git a/tests/core/strings/test_core_strings.odin b/tests/core/strings/test_core_strings.odin index f4947676591..0ee2b3eb9b8 100644 --- a/tests/core/strings/test_core_strings.odin +++ b/tests/core/strings/test_core_strings.odin @@ -2,81 +2,42 @@ package test_core_strings import "core:strings" import "core:testing" -import "core:fmt" -import "core:os" 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_index_any_small_string_not_found :: proc(t: ^testing.T) { 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_index_any_larger_string_not_found :: proc(t: ^testing.T) { 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_index_any_small_string_found :: proc(t: ^testing.T) { 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_index_any_larger_string_found :: proc(t: ^testing.T) { 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_last_index_any_small_string_found :: proc(t: ^testing.T) { 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_last_index_any_small_string_not_found :: proc(t: ^testing.T) { 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 { @@ -100,9 +61,12 @@ test_cut :: proc(t: ^testing.T) { res := strings.cut(test.input, test.offset, test.length) defer delete(res) - msg := fmt.tprintf("cut(\"%v\", %v, %v) expected to return \"%v\", got \"%v\"", - test.input, test.offset, test.length, test.output, res) - expect(t, res == test.output, msg) + testing.expectf( + t, + 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, } -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}{ .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}, } -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) } -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) } // 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_upper_snake_case :: proc(r: string, allocator: runtime.Allocator) -> (string, mem.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_upper_kebab_case :: proc(r: string, allocator: runtime.Allocator) -> (string, mem.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_pascal_case :: proc(r: string, allocator: runtime.Allocator) -> (string, mem.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_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, runtime.Allocator_Error) { return strings.to_upper_snake_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, runtime.Allocator_Error) { return strings.to_upper_kebab_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, runtime.Allocator_Error) { return strings.to_pascal_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_case_conversion :: proc(t: ^testing.T) { for entry in test_cases { for test_case, case_kind in test_cases { result, err := entry.p(test_case.s, context.allocator) - msg := fmt.tprintf("ERROR: We got the allocation error '{}'\n", err) - expect(t, err == nil, msg) + testing.expectf(t, err == nil, "ERROR: We got the allocation error '{}'\n", err) defer delete(result) - msg = fmt.tprintf("ERROR: Input `{}` to converter {} does not match `{}`, got `{}`.\n", test_case.s, case_kind, entry.s, result) - expect(t, result == entry.s, msg) + testing.expectf(t, result == entry.s, "ERROR: Input `{}` to converter {} does not match `{}`, got `{}`.\n", test_case.s, case_kind, entry.s, result) } } } \ No newline at end of file diff --git a/tests/core/text/i18n/test_core_text_i18n.odin b/tests/core/text/i18n/test_core_text_i18n.odin index dcbdeb0c4f4..f6cffc31832 100644 --- a/tests/core/text/i18n/test_core_text_i18n.odin +++ b/tests/core/text/i18n/test_core_text_i18n.odin @@ -1,31 +1,9 @@ package test_core_text_i18n -import "core:mem" -import "core:fmt" -import "core:os" +import "base:runtime" import "core:testing" 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 Test :: struct { @@ -37,25 +15,28 @@ Test :: struct { Test_Suite :: struct { 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, err: i18n.Error, options: i18n.Parse_Options, tests: []Test, } -// 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_SUITE_PATH :: ODIN_ROOT + "tests/core/assets/I18N/" + +@(test) +test_custom_pluralizer :: proc(t: ^testing.T) { + // 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 + } } -} -TESTS := []Test_Suite{ - { - file = "assets/I18N/plur.mo", + test(t, { + file = TEST_SUITE_PATH + "plur.mo", loader = i18n.parse_mo_file, plural = plur_mo_pluralizer, tests = { @@ -66,14 +47,16 @@ TESTS := []Test_Suite{ {"", "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 B", 42}, - // This isn't in the catalog, so should ruturn the key. {"", "Come visit us on Discord!", "Come visit us on Discord!", 1}, }, - }, + }) +} - { - file = "assets/I18N/mixed_context.mo", +@(test) +test_mixed_context :: proc(t: ^testing.T) { + test(t, { + file = TEST_SUITE_PATH + "mixed_context.mo", loader = i18n.parse_mo_file, plural = nil, tests = { @@ -84,19 +67,25 @@ TESTS := []Test_Suite{ // This isn't in the catalog, so should ruturn the key. {"", "Come visit us on Discord!", "Come visit us on Discord!", 1}, }, - }, + }) +} - { - file = "assets/I18N/mixed_context.mo", +@(test) +test_mixed_context_dupe :: proc(t: ^testing.T) { + test(t, { + file = TEST_SUITE_PATH + "mixed_context.mo", loader = i18n.parse_mo_file, plural = nil, // Message1 exists twice, once within Context, which has been merged into "" err = .Duplicate_Key, options = {merge_sections = true}, - }, + }) +} - { - file = "assets/I18N/nl_NL.mo", +@(test) +test_nl_mo :: proc(t: ^testing.T) { + test(t, { + file = TEST_SUITE_PATH + "nl_NL.mo", loader = i18n.parse_mo_file, plural = nil, // Default pluralizer tests = { @@ -111,12 +100,13 @@ TESTS := []Test_Suite{ // This isn't in the catalog, so should ruturn the key. {"", "Come visit us on Discord!", "Come visit us on Discord!", 1}, }, - }, - + }) +} - // QT Linguist with default loader options. - { - file = "assets/I18N/nl_NL-qt-ts.ts", +@(test) +test_qt_linguist :: proc(t: ^testing.T) { + test(t, { + file = TEST_SUITE_PATH + "nl_NL-qt-ts.ts", loader = i18n.parse_qt_linguist_file, plural = nil, // Default pluralizer tests = { @@ -131,11 +121,13 @@ TESTS := []Test_Suite{ {"", "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. - { - file = "assets/I18N/nl_NL-qt-ts.ts", +@(test) +test_qt_linguist_merge_sections :: proc(t: ^testing.T) { + test(t, { + file = TEST_SUITE_PATH + "nl_NL-qt-ts.ts", loader = i18n.parse_qt_linguist_file, plural = nil, // Default pluralizer 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)", 42}, }, - }, + }) +} - // QT Linguist, merging sections. Expecting .Duplicate_Key error because same key exists in more than 1 section. - { - file = "assets/I18N/duplicate-key.ts", +@(test) +test_qt_linguist_duplicate_key_err :: proc(t: ^testing.T) { + 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, plural = nil, // Default pluralizer options = {merge_sections = true}, err = .Duplicate_Key, - }, + }) +} - // QT Linguist, not merging sections. Shouldn't return error despite same key existing in more than 1 section. - { - file = "assets/I18N/duplicate-key.ts", +@(test) +test_qt_linguist_duplicate_key :: proc(t: ^testing.T) { + 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, plural = nil, // Default pluralizer - }, + }) } -@test -tests :: proc(t: ^testing.T) { - cat: ^i18n.Translation - err: i18n.Error - - for suite in TESTS { - cat, err = suite.loader(suite.file, suite.options, suite.plural, context.allocator) - - msg := fmt.tprintf("Expected loading %v to return %v, got %v", suite.file, suite.err, err) - 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) - } +test :: proc(t: ^testing.T, suite: Test_Suite, loc := #caller_location) { + cat, err := suite.loader(suite.file, suite.options, suite.plural, context.allocator) + testing.expectf(t, err == suite.err, "Expected loading %v to return %v, got %v", suite.file, suite.err, err, loc=loc) - 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) + if err == .None { + for test in suite.tests { + val := T(test.section, test.key, test.n, cat) + 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) } } + i18n.destroy(cat) } \ No newline at end of file diff --git a/tests/core/text/match/test_core_text_match.odin b/tests/core/text/match/test_core_text_match.odin index b72190f7802..5716b06fb64 100644 --- a/tests/core/text/match/test_core_text_match.odin +++ b/tests/core/text/match/test_core_text_match.odin @@ -2,31 +2,6 @@ package test_strlib import "core:text/match" 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 @test @@ -61,18 +36,17 @@ test_find :: proc(t: ^testing.T) { { "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) start, end, ok := match.matcher_find(&matcher) success := entry.match.ok == ok && start == entry.match.start && end == entry.match.end - if failed(t, success) { - logf(t, "Find %d failed!\n", i) - logf(t, "\tHAYSTACK %s\tPATTERN %s\n", entry.s, entry.p) - logf(t, "\tSTART: %d == %d?\n", entry.match.start, start) - logf(t, "\tEND: %d == %d?\n", entry.match.end, end) - logf(t, "\tErr: %v\tLength %d\n", matcher.err, matcher.captures_length) - } + testing.expectf( + t, + success, + "HAYSTACK %q PATTERN %q, START: %d == %d? END: %d == %d? Err: %v Length %d", + entry.s, entry.p, entry.match.start, start, entry.match.end, end, matcher.err, matcher.captures_length, + ) } } @@ -178,17 +152,17 @@ test_match :: proc(t: ^testing.T) { { "testing _this_ out", "%b_", "", false }, } - for entry, i in ENTRIES { + for entry in ENTRIES { matcher := match.matcher_init(entry.s, entry.p) result, ok := match.matcher_match(&matcher) success := entry.ok == ok && result == entry.result - if failed(t, success) { - logf(t, "Match %d failed!\n", i) - logf(t, "\tHAYSTACK %s\tPATTERN %s\n", entry.s, entry.p) - logf(t, "\tResults: WANTED %s\tGOT %s\n", entry.result, result) - logf(t, "\tErr: %v\tLength %d\n", matcher.err, matcher.captures_length) - } + testing.expectf( + t, + success, + "HAYSTACK %q PATTERN %q WANTED %q GOT %q Err: %v Length %d", + 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) { length, err := match.find_aux(haystack, test.pattern, 0, false, &test.captures) result := len(comp) == length && err == .OK - if failed(t, result == true) { - logf(t, "Captures Compare Failed!\n") - logf(t, "\tErr: %v\n", err) - logf(t, "\tLengths: %v != %v\n", len(comp), length) - } + testing.expectf( + t, + result, + "Captures Compare Failed! Lengths: %v != %v Err: %v", + len(comp), length, err, + ) for i in 0.. %s != %s\n", comp[i], text) - } + testing.expectf( + 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) result := length > 0 && err == .OK - if failed(t, result == ok) { - logf(t, "Capture match failed!\n") - logf(t, "\tErr: %v\n", err) - logf(t, "\tLength: %v\n", length) - } + testing.expectf( + t, + result == ok, + "Capture match failed! Length: %v Pattern: %q Haystack: %q Err: %v", + length, test.pattern, haystack, err, + ) } temp := Temp { pattern = "(one).+" } @@ -253,15 +232,8 @@ test_captures :: proc(t: ^testing.T) { cap2 := captures[2] text1 := haystack[cap1.byte_start:cap1.byte_end] text2 := haystack[cap2.byte_start:cap2.byte_end] - expect(t, text1 == "233", "Multi-Capture failed at 1") - 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) + testing.expect(t, text1 == "233", "Multi-Capture failed at 1") + testing.expect(t, text2 == "hello", "Multi-Capture failed at 2") } } @@ -298,9 +270,9 @@ test_gmatch :: proc(t: ^testing.T) { @test test_gsub :: proc(t: ^testing.T) { 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) - expect(t, result == "345123345", "GSUB 1: failed") + testing.expect(t, result == "345123345", "GSUB 1: failed") } @test @@ -313,10 +285,12 @@ test_gfind :: proc(t: ^testing.T) { index: int for word in match.gfind(s, pattern, &captures) { - if failed(t, output[index] == word) { - logf(t, "GFIND %d failed!\n", index) - logf(t, "\t%s != %s\n", output[index], word) - } + testing.expectf( + t, + output[index] == word, + "GFIND %d failed! %q != %q", + index, output[index], word, + ) index += 1 } } @@ -332,11 +306,12 @@ test_frontier :: proc(t: ^testing.T) { call :: proc(data: rawptr, word: string, haystack: string, captures: []match.Match) { temp := cast(^Temp) data - if failed(temp.t, word == temp.output[temp.index]) { - logf(temp.t, "GSUB_WITH %d failed!\n", temp.index) - logf(temp.t, "\t%s != %s\n", temp.output[temp.index], word) - } - + testing.expectf( + temp.t, + word == temp.output[temp.index], + "GSUB_WITH %d failed! %q != %q", + temp.index, temp.output[temp.index], word, + ) temp.index += 1 } @@ -369,31 +344,21 @@ test_case_insensitive :: proc(t: ^testing.T) { pattern := match.pattern_case_insensitive("test", 256, context.temp_allocator) goal := "[tT][eE][sS][tT]" - if failed(t, pattern == goal) { - logf(t, "Case Insensitive Pattern doesn't match result\n") - logf(t, "\t%s != %s\n", pattern, goal) - } + testing.expectf( + t, + pattern == goal, + "Case Insensitive Pattern doesn't match result. %q != %q", + pattern, goal, + ) } } -main :: proc() { - t: testing.T - stream := os.stream_from_handle(os.stdout) - w := io.to_writer(stream) - t.w = w - - test_find(&t) - 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) - } +@(private) +gmatch_check :: proc(t: ^testing.T, index: int, a: []string, b: string) { + testing.expectf( + t, + a[index] == b, + "GMATCH %d failed! %q != %q", + index, a[index], b, + ) } \ No newline at end of file diff --git a/tests/core/thread/test_core_thread.odin b/tests/core/thread/test_core_thread.odin index c0c7396a78d..0b77ad51128 100644 --- a/tests/core/thread/test_core_thread.odin +++ b/tests/core/thread/test_core_thread.odin @@ -2,39 +2,7 @@ package test_core_thread import "core:testing" import "core:thread" -import "core:fmt" -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) - } -} +import "base:intrinsics" @(test) poly_data_test :: proc(_t: ^testing.T) { @@ -46,7 +14,7 @@ poly_data_test :: proc(_t: ^testing.T) { b: [MAX]byte = 8 t1 := thread.create_and_start_with_poly_data(b, proc(b: [MAX]byte) { 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) @@ -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) { b_expect: [3]uintptr = 1 b2_expect: [MAX / 2]byte = 3 - 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, b == b_expect, "thread poly data not correct") + testing.expect(poly_data_test_t, b2 == b2_expect, "thread poly data not correct") }) defer free(t2) @@ -64,21 +32,21 @@ poly_data_test :: proc(_t: ^testing.T) { b_expect: [3]uintptr = 1 b2_expect: [MAX / 2]byte = 3 - 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") - expect(poly_data_test_t, b3 == 333, "thread poly data not correct") + testing.expect(poly_data_test_t, b == b_expect, "thread poly data not correct") + testing.expect(poly_data_test_t, b2 == b2_expect, "thread poly data not correct") + testing.expect(poly_data_test_t, b3 == 333, "thread poly data not correct") }) 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) { b_expect: [3]uintptr = 1 - 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") - 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, n == 111, "thread poly data not correct") + testing.expect(poly_data_test_t, b == b_expect, "thread poly data not correct") + testing.expect(poly_data_test_t, n2 == 333, "thread poly data not correct") + testing.expect(poly_data_test_t, n4 == 5, "thread poly data not correct") }) defer free(t4) thread.join_multiple(t1, t2, t3, t4) -} +} \ No newline at end of file diff --git a/tests/core/time/test_core_time.odin b/tests/core/time/test_core_time.odin index c6c6869a7b4..aeae44ca196 100644 --- a/tests/core/time/test_core_time.odin +++ b/tests/core/time/test_core_time.odin @@ -1,68 +1,17 @@ package test_core_time -import "core:fmt" -import "core:mem" -import "core:os" import "core:testing" import "core:time" import dt "core:time/datetime" 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_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.") - 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.") - 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_ordinal_to_date(dt.unsafe_date_to_ordinal(dt.MIN_DATE)) == dt.MIN_DATE, "Roundtripping MIN_DATE failed.") + testing.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_ordinal_to_date(dt.unsafe_date_to_ordinal(dt.MAX_DATE)) == dt.MAX_DATE, "Roundtripping MAX_DATE 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 if test.apply_offset { 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) - expect(t, test.consumed == consumed, msg) + testing.expectf( + 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 { - 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)) - expect(t, test.is_leap == is_leap, "Expected a leap second, got none.") + testing.expectf( + 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 { 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) - expect(t, test.consumed == consumed, msg) + testing.expectf( + 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 { - 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)) - expect(t, test.utc_offset == offset, fmt.tprintf("UTC offset didn't match. Expected %v, got %v", test.utc_offset, offset)) - expect(t, test.is_leap == is_leap, "Expected a leap second, got none.") + testing.expectf( + t, test.datetime == res, + "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 if test.apply_offset { 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) - expect(t, test.consumed == consumed, msg) + testing.expectf( + 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 { - 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)) - expect(t, test.is_leap == is_leap, "Expected a leap second, got none.") + testing.expectf( + 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 { 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) - expect(t, test.consumed == consumed, msg) + testing.expectf( + 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 { - 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)) - expect(t, test.utc_offset == offset, fmt.tprintf("UTC offset didn't match. Expected %v, got %v", test.utc_offset, offset)) - expect(t, test.is_leap == is_leap, "Expected a leap second, got none.") + testing.expectf( + t, test.datetime == res, + "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) { 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) 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.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, + ) } \ No newline at end of file diff --git a/tests/internal/Makefile b/tests/internal/Makefile index 09182cd236a..fb22767d221 100644 --- a/tests/internal/Makefile +++ b/tests/internal/Makefile @@ -1,23 +1,22 @@ ODIN=../../odin +COMMON=-file -vet -strict-style -o:minimal -all: all_bsd asan_test - -all_bsd: rtti_test map_test pow_test 128_test string_compare_test +all: asan_test rtti_test map_test pow_test 128_test string_compare_test rtti_test: - $(ODIN) run test_rtti.odin -file -vet -strict-style -o:minimal + $(ODIN) test test_rtti.odin $(COMMON) map_test: - $(ODIN) run test_map.odin -file -vet -strict-style -o:minimal + $(ODIN) test test_map.odin $(COMMON) pow_test: - $(ODIN) run test_pow.odin -file -vet -strict-style -o:minimal + $(ODIN) test test_pow.odin $(COMMON) 128_test: - $(ODIN) run test_128.odin -file -vet -strict-style -o:minimal + $(ODIN) test test_128.odin $(COMMON) asan_test: - $(ODIN) run test_asan.odin -file -sanitize:address -debug + $(ODIN) test test_asan.odin $(COMMON) -sanitize:address -debug string_compare_test: - $(ODIN) run test_string_compare.odin -file -vet -strict-style -o:minimal + $(ODIN) test test_string_compare.odin $(COMMON) \ No newline at end of file diff --git a/tests/internal/build.bat b/tests/internal/build.bat index 29b3e36c329..edaa2684bdf 100644 --- a/tests/internal/build.bat +++ b/tests/internal/build.bat @@ -1,10 +1,9 @@ @echo off set PATH_TO_ODIN==..\..\odin -rem %PATH_TO_ODIN% run test_rtti.odin -file -vet -strict-style -o:minimal || exit /b -%PATH_TO_ODIN% run test_map.odin -file -vet -strict-style -o:minimal || exit /b -rem -define:SEED=42 -%PATH_TO_ODIN% run test_pow.odin -file -vet -strict-style -o:minimal || exit /b - -%PATH_TO_ODIN% run test_128.odin -file -vet -strict-style -o:minimal || exit /b - -%PATH_TO_ODIN% run test_string_compare.odin -file -vet -strict-style -o:minimal || exit /b \ No newline at end of file +set COMMON=-file -vet -strict-style -o:minimal +%PATH_TO_ODIN% test test_rtti.odin %COMMON% || exit /b +%PATH_TO_ODIN% test test_map.odin %COMMON% || 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% test test_128.odin %COMMON% || exit /b +%PATH_TO_ODIN% test test_string_compare.odin %COMMON% || exit /b \ No newline at end of file diff --git a/tests/internal/test_128.odin b/tests/internal/test_128.odin index 11ef068edfd..1aa7487b64b 100644 --- a/tests/internal/test_128.odin +++ b/tests/internal/test_128.odin @@ -1,41 +1,7 @@ package test_128 -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_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_128_align :: proc(t: ^testing.T) { 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}} - expect(t, list[0].x == 0, fmt.tprintf("[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].x == 0, "[0].x (%v) != 0", list[0].x) + 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)) - expect(t, list[1].y == 0, fmt.tprintf("[1].y (%v) != 0", list[1].y)) + testing.expectf(t, list[1].x == 1, "[1].x (%v) != 1", list[1].x) + 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)) - expect(t, list[2].y == 0, fmt.tprintf("[2].y (%v) != 0", list[2].y)) + testing.expectf(t, list[2].x == 2, "[2].x (%v) != 2", list[2].x) + 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)) - expect(t, list[3].y == 0, fmt.tprintf("[3].y (%v) != 0", list[3].y)) + testing.expectf(t, list[3].x == 3, "[3].x (%v) != 3", list[3].x) + testing.expectf(t, list[3].y == 0, "[3].y (%v) != 0", list[3].y) } diff --git a/tests/internal/test_asan.odin b/tests/internal/test_asan.odin index 2384ada76a4..66f3c6547fe 100644 --- a/tests/internal/test_asan.odin +++ b/tests/internal/test_asan.odin @@ -1,42 +1,7 @@ // Intended to contain code that would trigger asan easily if the abi was set up badly. package test_asan -import "core:fmt" 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_12_bytes :: proc(t: ^testing.T) { @@ -45,9 +10,9 @@ test_12_bytes :: proc(t: ^testing.T) { } a, b, ok := internal() - expect(t, a == max(f32), fmt.tprintf("a (%v) != max(f32)", a)) - expect(t, b == 0, fmt.tprintf("b (%v) != 0", b)) - expect(t, ok, fmt.tprintf("ok (%v) != true", ok)) + testing.expectf(t, a == max(f32), "a (%v) != max(f32)", a) + testing.expectf(t, b == 0, "b (%v) != 0", b) + testing.expectf(t, ok, "ok (%v) != true", ok) } @(test) @@ -57,6 +22,6 @@ test_12_bytes_two :: proc(t: ^testing.T) { } a, b := internal() - expect(t, a == 100., fmt.tprintf("a (%v) != 100.", a)) - expect(t, b == max(int), fmt.tprintf("b (%v) != max(int)", b)) + testing.expectf(t, a == 100., "a (%v) != 100.", a) + testing.expectf(t, b == max(int), "b (%v) != max(int)", b) } diff --git a/tests/internal/test_map.odin b/tests/internal/test_map.odin index 7d1dbf470e0..fa13244acbb 100644 --- a/tests/internal/test_map.odin +++ b/tests/internal/test_map.odin @@ -1,26 +1,22 @@ package test_internal_map -import "core:fmt" +import "core:log" import "base:intrinsics" import "core:math/rand" -import "core:mem" -import "core:os" import "core:testing" -seed: u64 - ENTRY_COUNTS := []int{11, 101, 1_001, 10_001, 100_001, 1_000_001} @test map_insert_random_key_value :: proc(t: ^testing.T) { seed_incr := u64(0) 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 defer delete(m) unique_keys := 0 - r := rand.create(seed + seed_incr) + r := rand.create(t.seed + seed_incr) for _ in 0.. 5 { - fmt.println("... and more") + log.info("... and more") 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 @@ -65,12 +61,12 @@ map_insert_random_key_value :: proc(t: ^testing.T) { map_update_random_key_value :: proc(t: ^testing.T) { seed_incr := u64(0) 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 defer delete(m) unique_keys := 0 - r := rand.create(seed + seed_incr) + r := rand.create(t.seed + seed_incr) for _ in 0.. 5 { - fmt.println("... and more") + log.info("... and more") 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 @@ -127,12 +123,12 @@ map_update_random_key_value :: proc(t: ^testing.T) { map_delete_random_key_value :: proc(t: ^testing.T) { seed_incr := u64(0) 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 defer delete(m) unique_keys := 0 - r := rand.create(seed + seed_incr) + r := rand.create(t.seed + seed_incr) for _ in 0.. 5 { - fmt.println("... and more") + log.info("... and more") 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 { if k not_in m { num_fails += 1 if num_fails > 5 { - fmt.println("... and more") + log.info("... and more") 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 { num_fails += 1 if num_fails > 5 { - fmt.println("... and more") + log.info("... and more") 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) { seed_incr := u64(0) 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{} defer delete(m) unique_keys := 0 - r := rand.create(seed + seed_incr) + r := rand.create(t.seed + seed_incr) for _ in 0.. 5 { - fmt.println("... and more") + log.info("... and more") 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 @@ -252,12 +248,12 @@ set_insert_random_key_value :: proc(t: ^testing.T) { set_delete_random_key_value :: proc(t: ^testing.T) { seed_incr := u64(0) 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{} defer delete(m) unique_keys := 0 - r := rand.create(seed + seed_incr) + r := rand.create(t.seed + seed_incr) for _ in 0.. 5 { - fmt.println("... and more") + log.info("... and more") 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 { if k not_in m { num_fails += 1 if num_fails > 5 { - fmt.println("... and more") + log.info("... and more") 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 } -} - -// -------- -------- -------- -------- -------- -------- -------- -------- -------- -------- - -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) - } -} +} \ No newline at end of file diff --git a/tests/internal/test_pow.odin b/tests/internal/test_pow.odin index 70b81258da1..609087fb220 100644 --- a/tests/internal/test_pow.odin +++ b/tests/internal/test_pow.odin @@ -1,8 +1,7 @@ package test_internal_math_pow -import "core:fmt" +@(require) import "core:log" import "core:math" -import "core:os" import "core:testing" @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 _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)) v2 := math.pow2_f32(exp) _v1 := transmute(u32)v1 _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)) @@ -36,46 +35,11 @@ pow_test :: proc(t: ^testing.T) { when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 { 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 } } - - expect(t, _v1 == _v2, fmt.tprintf("Expected math.pow2_f16(%d) == math.pow(2, %d) (= %04x), got %04x", exp, exp, _v1, _v2)) + testing.expectf(t, _v1 == _v2, "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) - } -} +} \ No newline at end of file diff --git a/tests/internal/test_rtti.odin b/tests/internal/test_rtti.odin index 12f64462b72..ec5508208a3 100644 --- a/tests/internal/test_rtti.odin +++ b/tests/internal/test_rtti.odin @@ -1,11 +1,8 @@ package test_internal_rtti import "core:fmt" -import "core:mem" -import "core:os" import "core:testing" - Buggy_Struct :: struct { a: int, b: bool, @@ -28,74 +25,22 @@ rtti_test :: proc(t: ^testing.T) { for v, i in g_b { 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 for v, i in l_b { 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))) - expect(t, size_of(g_buggy) == 40, fmt.tprintf("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(Buggy_Struct) == 40, "Expected size_of(Buggy_Struct) == 40, got %v", size_of(Buggy_Struct)) + testing.expectf(t, size_of(g_buggy) == 40, "Expected size_of(g_buggy) == 40, got %v", size_of(g_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) 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)) - expect(t, l_s == EXPECTED_REPR, fmt.tprintf("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) - } + testing.expectf(t, g_s == EXPECTED_REPR, "Expected fmt.tprintf(\"%%s\", g_s)) to return \"%v\", got \"%v\"", EXPECTED_REPR, g_s) + testing.expectf(t, l_s == EXPECTED_REPR, "Expected fmt.tprintf(\"%%s\", l_s)) to return \"%v\", got \"%v\"", EXPECTED_REPR, l_s) } \ No newline at end of file diff --git a/tests/internal/test_string_compare.odin b/tests/internal/test_string_compare.odin index ff65b41c2af..63b62cc968d 100644 --- a/tests/internal/test_string_compare.odin +++ b/tests/internal/test_string_compare.odin @@ -1,7 +1,5 @@ package test_internal_string_compare -import "core:fmt" -import "core:os" import "core:testing" Op :: enum { Eq, Lt, Gt } @@ -29,65 +27,31 @@ string_compare :: proc(t: ^testing.T) { for res, op in v.res { switch op { case .Eq: - expect(t, (v.a == v.b) == res, fmt.tprintf("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, (v.a == v.b) == res, "Expected cstring(\"%v\") == cstring(\"%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 - expect(t, (v.a != v.b) == !res, fmt.tprintf("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, (v.a != v.b) == !res, "Expected cstring(\"%v\") != cstring(\"%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: - expect(t, (v.a < v.b) == res, fmt.tprintf("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, (v.a < v.b) == res, "Expected cstring(\"%v\") < cstring(\"%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 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)) - 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, (v.a <= v.b) == lteq, "Expected cstring(\"%v\") <= cstring(\"%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: - expect(t, (v.a > v.b) == res, fmt.tprintf("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, (v.a > v.b) == res, "Expected cstring(\"%v\") > cstring(\"%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 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)) - 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, (v.a >= v.b) == gteq, "Expected cstring(\"%v\") >= cstring(\"%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) - } } \ No newline at end of file diff --git a/tests/issues/run.bat b/tests/issues/run.bat index 41c52c02f2e..f3e3daba96a 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -3,19 +3,19 @@ if not exist "build\" mkdir build pushd build -set COMMON=-collection:tests=..\.. +set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style @echo on -..\..\..\odin test ..\test_issue_829.odin %COMMON% -file || exit /b -..\..\..\odin test ..\test_issue_1592.odin %COMMON% -file || exit /b -..\..\..\odin test ..\test_issue_2056.odin %COMMON% -file || exit /b -..\..\..\odin test ..\test_issue_2087.odin %COMMON% -file || exit /b -..\..\..\odin build ..\test_issue_2113.odin %COMMON% -file -debug || exit /b -..\..\..\odin test ..\test_issue_2466.odin %COMMON% -file || exit /b -..\..\..\odin test ..\test_issue_2615.odin %COMMON% -file || exit /b -..\..\..\odin test ..\test_issue_2637.odin %COMMON% -file || exit /b -..\..\..\odin test ..\test_issue_2666.odin %COMMON% -file || exit /b +..\..\..\odin test ..\test_issue_829.odin %COMMON% || exit /b +..\..\..\odin test ..\test_issue_1592.odin %COMMON% || exit /b +..\..\..\odin test ..\test_issue_2056.odin %COMMON% || exit /b +..\..\..\odin test ..\test_issue_2087.odin %COMMON% || exit /b +..\..\..\odin build ..\test_issue_2113.odin %COMMON% -debug || exit /b +..\..\..\odin test ..\test_issue_2466.odin %COMMON% || exit /b +..\..\..\odin test ..\test_issue_2615.odin %COMMON% || exit /b +..\..\..\odin test ..\test_issue_2637.odin %COMMON% || exit /b +..\..\..\odin test ..\test_issue_2666.odin %COMMON% || exit /b @echo off diff --git a/tests/issues/run.sh b/tests/issues/run.sh index 6d53388a7d0..24b388b0704 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -4,22 +4,22 @@ set -eu mkdir -p build pushd build ODIN=../../../odin -COMMON="-collection:tests=../.." +COMMON="-define:ODIN_TEST_FANCY=false -file -vet -strict-style" NO_NIL_ERR="Error: " set -x -$ODIN test ../test_issue_829.odin $COMMON -file -$ODIN test ../test_issue_1592.odin $COMMON -file -$ODIN test ../test_issue_2056.odin $COMMON -file -$ODIN test ../test_issue_2087.odin $COMMON -file -$ODIN build ../test_issue_2113.odin $COMMON -file -debug -$ODIN test ../test_issue_2466.odin $COMMON -file -$ODIN test ../test_issue_2615.odin $COMMON -file -$ODIN test ../test_issue_2637.odin $COMMON -file -$ODIN test ../test_issue_2666.odin $COMMON -file -if [[ $($ODIN build ../test_issue_2395.odin $COMMON -file 2>&1 >/dev/null | grep -c "$NO_NIL_ERR") -eq 2 ]] ; then +$ODIN test ../test_issue_829.odin $COMMON +$ODIN test ../test_issue_1592.odin $COMMON +$ODIN test ../test_issue_2056.odin $COMMON +$ODIN test ../test_issue_2087.odin $COMMON +$ODIN build ../test_issue_2113.odin $COMMON -debug +$ODIN test ../test_issue_2466.odin $COMMON +$ODIN test ../test_issue_2615.odin $COMMON +$ODIN test ../test_issue_2637.odin $COMMON +$ODIN test ../test_issue_2666.odin $COMMON +if [[ $($ODIN build ../test_issue_2395.odin $COMMON 2>&1 >/dev/null | grep -c "$NO_NIL_ERR") -eq 2 ]] ; then echo "SUCCESSFUL 1/1" else echo "SUCCESSFUL 0/1" diff --git a/tests/issues/test_issue_1592.odin b/tests/issues/test_issue_1592.odin index 800314a93b7..79eff33df27 100644 --- a/tests/issues/test_issue_1592.odin +++ b/tests/issues/test_issue_1592.odin @@ -1,7 +1,6 @@ // Tests issue #1592 https://github.com/odin-lang/Odin/issues/1592 package test_issues -import "core:fmt" import "core:testing" /* Original issue #1592 example */ @@ -31,428 +30,428 @@ true_result :: proc() -> bool { @test test_simple_const_false :: proc(t: ^testing.T) { if CONSTANT_FALSE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if (CONSTANT_FALSE) { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !CONSTANT_FALSE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if (!CONSTANT_FALSE) { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if !(CONSTANT_FALSE) { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if !!CONSTANT_FALSE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if CONSTANT_FALSE == true { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if CONSTANT_FALSE == false { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if !(CONSTANT_FALSE == true) { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if !(CONSTANT_FALSE == false) { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } } @test test_simple_const_true :: proc(t: ^testing.T) { if CONSTANT_TRUE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if (CONSTANT_TRUE) { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if !CONSTANT_TRUE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if (!CONSTANT_TRUE) { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if (!CONSTANT_TRUE) { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !(CONSTANT_TRUE) { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !!CONSTANT_TRUE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if CONSTANT_TRUE == true { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if CONSTANT_TRUE == false { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !(CONSTANT_TRUE == true) { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !(CONSTANT_TRUE == false) { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } } @test test_simple_proc_false :: proc(t: ^testing.T) { if false_result() { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !false_result() { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } } @test test_simple_proc_true :: proc(t: ^testing.T) { if true_result() { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if !true_result() { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } } @test test_const_false_const_false :: proc(t: ^testing.T) { if CONSTANT_FALSE || CONSTANT_FALSE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if CONSTANT_FALSE && CONSTANT_FALSE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !CONSTANT_FALSE || CONSTANT_FALSE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if !CONSTANT_FALSE && CONSTANT_FALSE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if CONSTANT_FALSE || !CONSTANT_FALSE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if CONSTANT_FALSE && !CONSTANT_FALSE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !(CONSTANT_FALSE || CONSTANT_FALSE) { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if !(CONSTANT_FALSE && CONSTANT_FALSE) { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } } @test test_const_false_const_true :: proc(t: ^testing.T) { if CONSTANT_FALSE || CONSTANT_TRUE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if CONSTANT_FALSE && CONSTANT_TRUE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !CONSTANT_FALSE || CONSTANT_TRUE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if !CONSTANT_FALSE && CONSTANT_TRUE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if CONSTANT_FALSE || !CONSTANT_TRUE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if CONSTANT_FALSE && !CONSTANT_TRUE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !(CONSTANT_FALSE || CONSTANT_TRUE) { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !(CONSTANT_FALSE && CONSTANT_TRUE) { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } } @test test_const_true_const_false :: proc(t: ^testing.T) { if CONSTANT_TRUE || CONSTANT_FALSE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if CONSTANT_TRUE && CONSTANT_FALSE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !CONSTANT_TRUE || CONSTANT_FALSE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !CONSTANT_TRUE && CONSTANT_FALSE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if CONSTANT_TRUE || !CONSTANT_FALSE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if CONSTANT_TRUE && !CONSTANT_FALSE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if !(CONSTANT_TRUE || CONSTANT_FALSE) { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !(CONSTANT_TRUE && CONSTANT_FALSE) { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } } @test test_const_true_const_true :: proc(t: ^testing.T) { if CONSTANT_TRUE || CONSTANT_TRUE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if CONSTANT_TRUE && CONSTANT_TRUE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if !CONSTANT_TRUE || CONSTANT_TRUE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if !CONSTANT_TRUE && CONSTANT_TRUE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if CONSTANT_TRUE || !CONSTANT_TRUE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if CONSTANT_TRUE && !CONSTANT_TRUE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !(CONSTANT_TRUE || CONSTANT_TRUE) { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !(CONSTANT_TRUE && CONSTANT_TRUE) { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } } @test test_proc_false_const_false :: proc(t: ^testing.T) { if false_result() || CONSTANT_FALSE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if false_result() && CONSTANT_FALSE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !(false_result() || CONSTANT_FALSE) { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if !(false_result() && CONSTANT_FALSE) { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } } @test test_proc_false_const_true :: proc(t: ^testing.T) { if false_result() || CONSTANT_TRUE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if false_result() && CONSTANT_TRUE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !(false_result() || CONSTANT_TRUE) { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !(false_result() && CONSTANT_TRUE) { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } } @test test_proc_true_const_false :: proc(t: ^testing.T) { if true_result() || CONSTANT_FALSE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if true_result() && CONSTANT_FALSE { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !(true_result() || CONSTANT_FALSE) { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !(true_result() && CONSTANT_FALSE) { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } } @test test_proc_true_const_true :: proc(t: ^testing.T) { if true_result() || CONSTANT_TRUE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if true_result() && CONSTANT_TRUE { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } else { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } if !(true_result() || CONSTANT_TRUE) { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } if !(true_result() && CONSTANT_TRUE) { - testing.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + testing.expect(t, false, "!false") } else { - testing.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + testing.expect(t, true, "!true") } } diff --git a/tests/issues/test_issue_2056.odin b/tests/issues/test_issue_2056.odin index 4869b557e23..06bc11fbae4 100644 --- a/tests/issues/test_issue_2056.odin +++ b/tests/issues/test_issue_2056.odin @@ -1,7 +1,6 @@ // Tests issue #2056 https://github.com/odin-lang/Odin/issues/2056 package test_issues -import "core:fmt" import "core:testing" @test @@ -12,9 +11,9 @@ test_scalar_matrix_conversion :: proc(t: ^testing.T) { for i in 0..<4 { for j in 0..<4 { if i == j { - testing.expect(t, m[i,j] == 1, fmt.tprintf("expected 1 at m[%d,%d], found %f\n", i, j, m[i,j])) + testing.expectf(t, m[i,j] == 1, "expected 1 at m[%d,%d], found %f\n", i, j, m[i,j]) } else { - testing.expect(t, m[i,j] == 0, fmt.tprintf("expected 0 at m[%d,%d], found %f\n", i, j, m[i,j])) + testing.expectf(t, m[i,j] == 0, "expected 0 at m[%d,%d], found %f\n", i, j, m[i,j]) } } } diff --git a/tests/issues/test_issue_2466.odin b/tests/issues/test_issue_2466.odin index 4810cfea979..f5987903a83 100644 --- a/tests/issues/test_issue_2466.odin +++ b/tests/issues/test_issue_2466.odin @@ -1,7 +1,6 @@ // Tests issue #2466 https://github.com/odin-lang/Odin/issues/2466 package test_issues -import "core:fmt" import "core:testing" Bug :: struct { @@ -16,7 +15,7 @@ test_compound_literal_local_reuse :: proc(t: ^testing.T) { val = v, arr = {42}, } - testing.expect(t, bug.val == 123, fmt.tprintf("expected 123, found %d", bug.val)) - testing.expect(t, bug.arr[0] == 42, fmt.tprintf("expected 42, found %d", bug.arr[0])) + testing.expectf(t, bug.val == 123, "expected 123, found %d", bug.val) + testing.expectf(t, bug.arr[0] == 42, "expected 42, found %d", bug.arr[0]) } diff --git a/tests/issues/test_issue_829.odin b/tests/issues/test_issue_829.odin index 273b3b3b52f..229d8e9b42d 100644 --- a/tests/issues/test_issue_829.odin +++ b/tests/issues/test_issue_829.odin @@ -1,7 +1,6 @@ // Tests issue #829 https://github.com/odin-lang/Odin/issues/829 package test_issues -import "core:fmt" import "core:testing" /* Original issue #829 example */ @@ -13,6 +12,6 @@ env : map[string]proc(a, b : int) -> int = { @(test) test_orig_ret :: proc(t: ^testing.T) { - r := fmt.tprint(env["+"](1, 2)) - testing.expect(t, r == "3", fmt.tprintf("%s: \"%s\" != \"3\"\n", #procedure, r)) -} + r := env["+"](1, 2) + testing.expectf(t, r == 3, "%q != 3", r) +} \ No newline at end of file diff --git a/tests/vendor/build.bat b/tests/vendor/build.bat index 693d344f4e0..84ab2f1ef85 100644 --- a/tests/vendor/build.bat +++ b/tests/vendor/build.bat @@ -1,8 +1,8 @@ @echo off -set COMMON=-show-timings -no-bounds-check -vet -strict-style +set COMMON=-show-timings -no-bounds-check -vet -strict-style -define:ODIN_TEST_FANCY=false set PATH_TO_ODIN==..\..\odin echo --- echo Running vendor:glfw tests echo --- -%PATH_TO_ODIN% run glfw %COMMON% -out:vendor_glfw.exe || exit /b \ No newline at end of file +%PATH_TO_ODIN% test glfw %COMMON% -out:vendor_glfw.exe || exit /b \ No newline at end of file diff --git a/tests/vendor/glfw/test_vendor_glfw.odin b/tests/vendor/glfw/test_vendor_glfw.odin index ce55ad7ef37..4254ebb0953 100644 --- a/tests/vendor/glfw/test_vendor_glfw.odin +++ b/tests/vendor/glfw/test_vendor_glfw.odin @@ -1,49 +1,21 @@ package test_vendor_glfw import "core:testing" -import "core:fmt" import "vendor:glfw" -import "core:os" GLFW_MAJOR :: 3 GLFW_MINOR :: 4 GLFW_PATCH :: 0 -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) { - fmt.printf("[%v] ", loc) - TEST_count += 1 - if !condition { - TEST_fail += 1 - fmt.println(message) - return - } - fmt.println(" PASS") - } - 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_glfw(&t) - - fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) - if TEST_fail > 0 { - os.exit(1) - } -} - @(test) test_glfw :: proc(t: ^testing.T) { major, minor, patch := glfw.GetVersion() - expect(t, major == GLFW_MAJOR && minor == GLFW_MINOR, fmt.tprintf("Expected GLFW.GetVersion: %v.%v.%v, got %v.%v.%v instead", GLFW_MAJOR, GLFW_MINOR, GLFW_PATCH, major, minor, patch)) + testing.expectf( + t, + major == GLFW_MAJOR && \ + minor == GLFW_MINOR, + "Expected GLFW.GetVersion: %v.%v.%v, got %v.%v.%v instead", + GLFW_MAJOR, GLFW_MINOR, GLFW_PATCH, + major, minor, patch, + ) }