|
@@ -0,0 +1,1350 @@
|
|
|
+package test_core_flags
|
|
|
+
|
|
|
+import "base:runtime"
|
|
|
+import "core:bytes"
|
|
|
+import "core:flags"
|
|
|
+import "core:fmt"
|
|
|
+@require import "core:log"
|
|
|
+import "core:math"
|
|
|
+@require import "core:net"
|
|
|
+import "core:os"
|
|
|
+import "core:strings"
|
|
|
+import "core:testing"
|
|
|
+import "core:time/datetime"
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_no_args :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: string,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args: []string
|
|
|
+ result := flags.parse(&s, args)
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_two_flags :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ i: string,
|
|
|
+ o: string,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-i:hellope", "-o:world" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.i, "hellope")
|
|
|
+ testing.expect_value(t, s.o, "world")
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_extra_arg :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: string,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-a:hellope", "world" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ err, ok := result.(flags.Parse_Error)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+ if ok {
|
|
|
+ testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Extra_Positional)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_assignment_oddities :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ s: string,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+
|
|
|
+ {
|
|
|
+ args := [?]string { "-s:=" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.s, "=")
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ args := [?]string { "-s=:" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.s, ":")
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ args := [?]string { "-" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ err, ok := result.(flags.Parse_Error)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+ if ok {
|
|
|
+ testing.expect_value(t, err.reason, flags.Parse_Error_Reason.No_Flag)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_string_into_int :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ n: int,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-n:hellope" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ err, ok := result.(flags.Parse_Error)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+ if ok {
|
|
|
+ testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_string_into_bool :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ b: bool,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-b:hellope" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ err, ok := result.(flags.Parse_Error)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+ if ok {
|
|
|
+ testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_all_bools :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: bool,
|
|
|
+ b: b8,
|
|
|
+ c: b16,
|
|
|
+ d: b32,
|
|
|
+ e: b64,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ s.a = true
|
|
|
+ s.c = true
|
|
|
+ args := [?]string { "-a:false", "-b:true", "-c:0", "-d", "-e:1" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.a, false)
|
|
|
+ testing.expect_value(t, s.b, true)
|
|
|
+ testing.expect_value(t, s.c, false)
|
|
|
+ testing.expect_value(t, s.d, true)
|
|
|
+ testing.expect_value(t, s.e, true)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_all_ints :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: u8,
|
|
|
+ b: i8,
|
|
|
+ c: u16,
|
|
|
+ d: i16,
|
|
|
+ e: u32,
|
|
|
+ f: i32,
|
|
|
+ g: u64,
|
|
|
+ i: i64,
|
|
|
+ j: u128,
|
|
|
+ k: i128,
|
|
|
+ }
|
|
|
+
|
|
|
+ s: S
|
|
|
+ args := [?]string {
|
|
|
+ fmt.tprintf("-a:%i", max(u8)),
|
|
|
+ fmt.tprintf("-b:%i", min(i8)),
|
|
|
+ fmt.tprintf("-c:%i", max(u16)),
|
|
|
+ fmt.tprintf("-d:%i", min(i16)),
|
|
|
+ fmt.tprintf("-e:%i", max(u32)),
|
|
|
+ fmt.tprintf("-f:%i", min(i32)),
|
|
|
+ fmt.tprintf("-g:%i", max(u64)),
|
|
|
+ fmt.tprintf("-i:%i", min(i64)),
|
|
|
+ fmt.tprintf("-j:%i", max(u128)),
|
|
|
+ fmt.tprintf("-k:%i", min(i128)),
|
|
|
+ }
|
|
|
+
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.a, max(u8))
|
|
|
+ testing.expect_value(t, s.b, min(i8))
|
|
|
+ testing.expect_value(t, s.c, max(u16))
|
|
|
+ testing.expect_value(t, s.d, min(i16))
|
|
|
+ testing.expect_value(t, s.e, max(u32))
|
|
|
+ testing.expect_value(t, s.f, min(i32))
|
|
|
+ testing.expect_value(t, s.g, max(u64))
|
|
|
+ testing.expect_value(t, s.i, min(i64))
|
|
|
+ testing.expect_value(t, s.j, max(u128))
|
|
|
+ testing.expect_value(t, s.k, min(i128))
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_all_floats :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: f16,
|
|
|
+ b: f32,
|
|
|
+ c: f64,
|
|
|
+ d: f64,
|
|
|
+ e: f64,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-a:100", "-b:3.14", "-c:-123.456", "-d:nan", "-e:inf" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.a, 100)
|
|
|
+ testing.expect_value(t, s.b, 3.14)
|
|
|
+ testing.expect_value(t, s.c, -123.456)
|
|
|
+ testing.expectf(t, math.is_nan(s.d), "expected NaN, got %v", s.d)
|
|
|
+ testing.expectf(t, math.is_inf(s.e, +1), "expected +Inf, got %v", s.e)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_all_enums :: proc(t: ^testing.T) {
|
|
|
+ E :: enum { A, B }
|
|
|
+ S :: struct {
|
|
|
+ nameless: enum { C, D },
|
|
|
+ named: E,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-nameless:D", "-named:B" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, cast(int)s.nameless, 1)
|
|
|
+ testing.expect_value(t, s.named, E.B)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_all_complex :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: complex32,
|
|
|
+ b: complex64,
|
|
|
+ c: complex128,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-a:1+0i", "-b:3+7i", "-c:NaNNaNi" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, real(s.a), 1)
|
|
|
+ testing.expect_value(t, imag(s.a), 0)
|
|
|
+ testing.expect_value(t, real(s.b), 3)
|
|
|
+ testing.expect_value(t, imag(s.b), 7)
|
|
|
+ testing.expectf(t, math.is_nan(real(s.c)), "expected NaN, got %v", real(s.c))
|
|
|
+ testing.expectf(t, math.is_nan(imag(s.c)), "expected NaN, got %v", imag(s.c))
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_all_quaternion :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: quaternion64,
|
|
|
+ b: quaternion128,
|
|
|
+ c: quaternion256,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-a:1+0i+1j+0k", "-b:3+7i+5j-3k", "-c:NaNNaNi+Infj-Infk" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+
|
|
|
+ raw_a := (cast(^runtime.Raw_Quaternion64)&s.a)
|
|
|
+ raw_b := (cast(^runtime.Raw_Quaternion128)&s.b)
|
|
|
+ raw_c := (cast(^runtime.Raw_Quaternion256)&s.c)
|
|
|
+
|
|
|
+ testing.expect_value(t, raw_a.real, 1)
|
|
|
+ testing.expect_value(t, raw_a.imag, 0)
|
|
|
+ testing.expect_value(t, raw_a.jmag, 1)
|
|
|
+ testing.expect_value(t, raw_a.kmag, 0)
|
|
|
+
|
|
|
+ testing.expect_value(t, raw_b.real, 3)
|
|
|
+ testing.expect_value(t, raw_b.imag, 7)
|
|
|
+ testing.expect_value(t, raw_b.jmag, 5)
|
|
|
+ testing.expect_value(t, raw_b.kmag, -3)
|
|
|
+
|
|
|
+ testing.expectf(t, math.is_nan(raw_c.real), "expected NaN, got %v", raw_c.real)
|
|
|
+ testing.expectf(t, math.is_nan(raw_c.imag), "expected NaN, got %v", raw_c.imag)
|
|
|
+ testing.expectf(t, math.is_inf(raw_c.jmag, +1), "expected +Inf, got %v", raw_c.jmag)
|
|
|
+ testing.expectf(t, math.is_inf(raw_c.kmag, -1), "expected -Inf, got %v", raw_c.kmag)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_all_bit_sets :: proc(t: ^testing.T) {
|
|
|
+ E :: enum {
|
|
|
+ Option_A,
|
|
|
+ Option_B,
|
|
|
+ }
|
|
|
+ S :: struct {
|
|
|
+ a: bit_set[0..<8],
|
|
|
+ b: bit_set[0..<16; u16],
|
|
|
+ c: bit_set[16..<18; rune],
|
|
|
+ d: bit_set[0..<1; i8],
|
|
|
+ e: bit_set[0..<128],
|
|
|
+ f: bit_set[-32..<32],
|
|
|
+ g: bit_set[E],
|
|
|
+ i: bit_set[E; u8],
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ {
|
|
|
+ args := [?]string {
|
|
|
+ "-a:10101",
|
|
|
+ "-b:0000_0000_0000_0001",
|
|
|
+ "-c:11",
|
|
|
+ "-d:___1",
|
|
|
+ "-e:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
|
|
|
+ "-f:1",
|
|
|
+ "-g:01",
|
|
|
+ "-i:1",
|
|
|
+ }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.a, bit_set[0..<8]{0, 2, 4})
|
|
|
+ testing.expect_value(t, s.b, bit_set[0..<16; u16]{15})
|
|
|
+ testing.expect_value(t, s.c, bit_set[16..<18; rune]{16, 17})
|
|
|
+ testing.expect_value(t, s.d, bit_set[0..<1; i8]{0})
|
|
|
+ testing.expect_value(t, s.e, bit_set[0..<128]{127})
|
|
|
+ testing.expect_value(t, s.f, bit_set[-32..<32]{-32})
|
|
|
+ testing.expect_value(t, s.g, bit_set[E]{E.Option_B})
|
|
|
+ testing.expect_value(t, s.i, bit_set[E; u8]{E.Option_A})
|
|
|
+ }
|
|
|
+ {
|
|
|
+ args := [?]string { "-d:11" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ err, ok := result.(flags.Parse_Error)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+ if ok {
|
|
|
+ testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_all_strings :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a, b, c: string,
|
|
|
+ d: cstring,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-a:hi", "-b:hellope", "-c:spaced out", "-d:cstr", "-d:cstr-overwrite" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ defer delete(s.d)
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.a, "hi")
|
|
|
+ testing.expect_value(t, s.b, "hellope")
|
|
|
+ testing.expect_value(t, s.c, "spaced out")
|
|
|
+ testing.expect_value(t, s.d, "cstr-overwrite")
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_runes :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a, b, c: rune,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-a:a", "-b:ツ", "-c:\U0010FFFF" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.a, 'a')
|
|
|
+ testing.expect_value(t, s.b, 'ツ')
|
|
|
+ testing.expect_value(t, s.c, '\U0010FFFF')
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_no_value :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: rune,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+
|
|
|
+ {
|
|
|
+ args := [?]string { "-a:" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ err, ok := result.(flags.Parse_Error)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+ if ok {
|
|
|
+ testing.expect_value(t, err.reason, flags.Parse_Error_Reason.No_Value)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ args := [?]string { "-a=" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ err, ok := result.(flags.Parse_Error)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+ if ok {
|
|
|
+ testing.expect_value(t, err.reason, flags.Parse_Error_Reason.No_Value)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_overflow :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: u8,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-a:256" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ err, ok := result.(flags.Parse_Error)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+ if ok {
|
|
|
+ testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_underflow :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: i8,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-a:-129" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ err, ok := result.(flags.Parse_Error)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+ if ok {
|
|
|
+ testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_arrays :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: [dynamic]string,
|
|
|
+ b: [dynamic]int,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-a:abc", "-b:1", "-a:foo", "-b:3" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ defer {
|
|
|
+ delete(s.a)
|
|
|
+ delete(s.b)
|
|
|
+ }
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, len(s.a), 2)
|
|
|
+ testing.expect_value(t, len(s.b), 2)
|
|
|
+
|
|
|
+ if len(s.a) < 2 || len(s.b) < 2 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ testing.expect_value(t, s.a[0], "abc")
|
|
|
+ testing.expect_value(t, s.a[1], "foo")
|
|
|
+ testing.expect_value(t, s.b[0], 1)
|
|
|
+ testing.expect_value(t, s.b[1], 3)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_varargs :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ varg: [dynamic]string,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "abc", "foo", "bar" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ defer delete(s.varg)
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, len(s.varg), 3)
|
|
|
+
|
|
|
+ if len(s.varg) < 3 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ testing.expect_value(t, s.varg[0], "abc")
|
|
|
+ testing.expect_value(t, s.varg[1], "foo")
|
|
|
+ testing.expect_value(t, s.varg[2], "bar")
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_mixed_varargs :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ input: string `args:"pos=0"`,
|
|
|
+ varg: [dynamic]string,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "abc", "foo", "bar" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ defer delete(s.varg)
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, len(s.varg), 2)
|
|
|
+
|
|
|
+ if len(s.varg) < 2 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ testing.expect_value(t, s.input, "abc")
|
|
|
+ testing.expect_value(t, s.varg[0], "foo")
|
|
|
+ testing.expect_value(t, s.varg[1], "bar")
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_maps :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: map[string]string,
|
|
|
+ b: map[string]int,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-a:abc=foo", "-b:bar=42" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ defer {
|
|
|
+ delete(s.a)
|
|
|
+ delete(s.b)
|
|
|
+ }
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, len(s.a), 1)
|
|
|
+ testing.expect_value(t, len(s.b), 1)
|
|
|
+
|
|
|
+ if len(s.a) < 1 || len(s.b) < 1 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ abc, has_abc := s.a["abc"]
|
|
|
+ bar, has_bar := s.b["bar"]
|
|
|
+
|
|
|
+ testing.expect(t, has_abc, "expected map to have `abc` key set")
|
|
|
+ testing.expect(t, has_bar, "expected map to have `bar` key set")
|
|
|
+ testing.expect_value(t, abc, "foo")
|
|
|
+ testing.expect_value(t, bar, 42)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_invalid_map_syntax :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: map[string]string,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-a:foo:42" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ err, ok := result.(flags.Parse_Error)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+ if ok {
|
|
|
+ testing.expect_value(t, err.reason, flags.Parse_Error_Reason.No_Value)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_underline_name_to_dash :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a_b: int,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-a-b:3" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.a_b, 3)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_tags_pos :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ b: int `args:"pos=1"`,
|
|
|
+ a: int `args:"pos=0"`,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "42", "99" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.a, 42)
|
|
|
+ testing.expect_value(t, s.b, 99)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_tags_name :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: int `args:"name=alice"`,
|
|
|
+ b: int `args:"name=bill"`,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-alice:1", "-bill:2" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.a, 1)
|
|
|
+ testing.expect_value(t, s.b, 2)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_tags_required :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: int,
|
|
|
+ b: int `args:"required"`,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-a:1" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ _, ok := result.(flags.Validation_Error)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_tags_required_pos :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: int `args:"pos=0,required"`,
|
|
|
+ b: int `args:"pos=1"`,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-b:5" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ _, ok := result.(flags.Validation_Error)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_tags_required_limit_min :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ n: [dynamic]int `args:"required=3"`,
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-n:1" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ defer delete(s.n)
|
|
|
+ _, ok := result.(flags.Validation_Error)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-n:3", "-n:5", "-n:7" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ defer delete(s.n)
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, len(s.n), 3)
|
|
|
+
|
|
|
+ if len(s.n) == 3 {
|
|
|
+ testing.expect_value(t, s.n[0], 3)
|
|
|
+ testing.expect_value(t, s.n[1], 5)
|
|
|
+ testing.expect_value(t, s.n[2], 7)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_tags_required_limit_min_max :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ n: [dynamic]int `args:"required=2<4"`,
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-n:1" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ defer delete(s.n)
|
|
|
+ _, ok := result.(flags.Validation_Error)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-n:1", "-n:2", "-n:3", "-n:4" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ defer delete(s.n)
|
|
|
+ _, ok := result.(flags.Validation_Error)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-n:3", "-n:5", "-n:7" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ defer delete(s.n)
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, len(s.n), 3)
|
|
|
+
|
|
|
+ if len(s.n) == 3 {
|
|
|
+ testing.expect_value(t, s.n[0], 3)
|
|
|
+ testing.expect_value(t, s.n[1], 5)
|
|
|
+ testing.expect_value(t, s.n[2], 7)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_tags_required_limit_max :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ n: [dynamic]int `args:"required=<4"`,
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ s: S
|
|
|
+ args: []string
|
|
|
+ result := flags.parse(&s, args)
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-n:1", "-n:2", "-n:3", "-n:4" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ defer delete(s.n)
|
|
|
+ _, ok := result.(flags.Validation_Error)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-n:3", "-n:5", "-n:7" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ defer delete(s.n)
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, len(s.n), 3)
|
|
|
+
|
|
|
+ if len(s.n) == 3 {
|
|
|
+ testing.expect_value(t, s.n[0], 3)
|
|
|
+ testing.expect_value(t, s.n[1], 5)
|
|
|
+ testing.expect_value(t, s.n[2], 7)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_tags_pos_out_of_order :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: int `args:"pos=2"`,
|
|
|
+ varg: [dynamic]int,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "1", "2", "3", "4" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ defer delete(s.varg)
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, len(s.varg), 3)
|
|
|
+
|
|
|
+ if len(s.varg) < 3 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ testing.expect_value(t, s.a, 3)
|
|
|
+ testing.expect_value(t, s.varg[0], 1)
|
|
|
+ testing.expect_value(t, s.varg[1], 2)
|
|
|
+ testing.expect_value(t, s.varg[2], 4)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_missing_flag :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: int,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-b" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ err, ok := result.(flags.Parse_Error)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+ if ok {
|
|
|
+ testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Missing_Flag)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_alt_syntax :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: int,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-a=3" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.a, 3)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_strict_returns_first_error :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ b: int,
|
|
|
+ c: int,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-a=3", "-b=3" }
|
|
|
+ result := flags.parse(&s, args[:], strict=true)
|
|
|
+ err, ok := result.(flags.Parse_Error)
|
|
|
+ testing.expect_value(t, s.b, 0)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+ if ok {
|
|
|
+ testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Missing_Flag)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_non_strict_returns_last_error :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: int,
|
|
|
+ b: int,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-a=foo", "-b=2", "-c=3" }
|
|
|
+ result := flags.parse(&s, args[:], strict=false)
|
|
|
+ err, ok := result.(flags.Parse_Error)
|
|
|
+ testing.expect_value(t, s.b, 2)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+ if ok {
|
|
|
+ testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Missing_Flag)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_map_overwrite :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ m: map[string]int,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-m:foo=3", "-m:foo=5" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ defer delete(s.m)
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, len(s.m), 1)
|
|
|
+ foo, has_foo := s.m["foo"]
|
|
|
+ testing.expect(t, has_foo, "expected map to have `foo` key set")
|
|
|
+ testing.expect_value(t, foo, 5)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_maps_of_arrays :: proc(t: ^testing.T) {
|
|
|
+ // Why you would ever want to do this, I don't know, but it's possible!
|
|
|
+ S :: struct {
|
|
|
+ m: map[string][dynamic]int,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+ args := [?]string { "-m:foo=1", "-m:foo=2", "-m:bar=3" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ defer {
|
|
|
+ for _, v in s.m {
|
|
|
+ delete(v)
|
|
|
+ }
|
|
|
+ delete(s.m)
|
|
|
+ }
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, len(s.m), 2)
|
|
|
+
|
|
|
+ if len(s.m) != 2 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ foo, has_foo := s.m["foo"]
|
|
|
+ bar, has_bar := s.m["bar"]
|
|
|
+
|
|
|
+ testing.expect_value(t, has_foo, true)
|
|
|
+ testing.expect_value(t, has_bar, true)
|
|
|
+
|
|
|
+ if has_foo {
|
|
|
+ testing.expect_value(t, len(foo), 2)
|
|
|
+ if len(foo) == 2 {
|
|
|
+ testing.expect_value(t, foo[0], 1)
|
|
|
+ testing.expect_value(t, foo[1], 2)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if has_bar {
|
|
|
+ testing.expect_value(t, len(bar), 1)
|
|
|
+ if len(bar) == 1 {
|
|
|
+ testing.expect_value(t, bar[0], 3)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_builtin_help_flag :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {}
|
|
|
+ s: S
|
|
|
+
|
|
|
+ args_short := [?]string { "-h" }
|
|
|
+ args_normal := [?]string { "-help" }
|
|
|
+
|
|
|
+ result := flags.parse(&s, args_short[:])
|
|
|
+ _, ok := result.(flags.Help_Request)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+
|
|
|
+ result = flags.parse(&s, args_normal[:])
|
|
|
+ _, ok = result.(flags.Help_Request)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+}
|
|
|
+
|
|
|
+// This test makes sure that if a positional argument is specified, it won't be
|
|
|
+// overwritten by an unspecified positional, which should follow the principle
|
|
|
+// of least surprise for the user.
|
|
|
+@(test)
|
|
|
+test_pos_nonoverlap :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: int `args:"pos=0"`,
|
|
|
+ b: int `args:"pos=1"`,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+
|
|
|
+ args := [?]string { "-a:3", "5" }
|
|
|
+
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.a, 3)
|
|
|
+ testing.expect_value(t, s.b, 5)
|
|
|
+}
|
|
|
+
|
|
|
+// This test ensures the underlying `bit_array` container handles many
|
|
|
+// arguments in a sane manner.
|
|
|
+@(test)
|
|
|
+test_pos_many_args :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ varg: [dynamic]int,
|
|
|
+ a: int `args:"pos=0,required"`,
|
|
|
+ b: int `args:"pos=64,required"`,
|
|
|
+ c: int `args:"pos=66,required"`,
|
|
|
+ d: int `args:"pos=129,required"`,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+
|
|
|
+ args: [dynamic]string
|
|
|
+ defer delete(s.varg)
|
|
|
+
|
|
|
+ for i in 0 ..< 130 { append(&args, fmt.aprintf("%i", 1 + i)) }
|
|
|
+ defer {
|
|
|
+ for a in args {
|
|
|
+ delete(a)
|
|
|
+ }
|
|
|
+ delete(args)
|
|
|
+ }
|
|
|
+
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+
|
|
|
+ testing.expect_value(t, s.a, 1)
|
|
|
+ for i in 1 ..< 63 { testing.expect_value(t, s.varg[i], 2 + i) }
|
|
|
+ testing.expect_value(t, s.b, 65)
|
|
|
+ testing.expect_value(t, s.varg[63], 66)
|
|
|
+ testing.expect_value(t, s.c, 67)
|
|
|
+ testing.expect_value(t, s.varg[64], 68)
|
|
|
+ testing.expect_value(t, s.varg[65], 69)
|
|
|
+ testing.expect_value(t, s.varg[66], 70)
|
|
|
+ for i in 67 ..< 126 { testing.expect_value(t, s.varg[i], 4 + i) }
|
|
|
+ testing.expect_value(t, s.d, 130)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_unix :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: string,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+
|
|
|
+ {
|
|
|
+ args := [?]string { "--a", "hellope" }
|
|
|
+
|
|
|
+ result := flags.parse(&s, args[:], .Unix)
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.a, "hellope")
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ args := [?]string { "-a", "hellope", "--a", "world" }
|
|
|
+
|
|
|
+ result := flags.parse(&s, args[:], .Unix)
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.a, "world")
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ args := [?]string { "-a=hellope" }
|
|
|
+
|
|
|
+ result := flags.parse(&s, args[:], .Unix)
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.a, "hellope")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_unix_variadic :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: [dynamic]int `args:"variadic"`,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+
|
|
|
+ args := [?]string { "--a", "7", "32", "11" }
|
|
|
+
|
|
|
+ result := flags.parse(&s, args[:], .Unix)
|
|
|
+ defer delete(s.a)
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, len(s.a), 3)
|
|
|
+
|
|
|
+ if len(s.a) < 3 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ testing.expect_value(t, s.a[0], 7)
|
|
|
+ testing.expect_value(t, s.a[1], 32)
|
|
|
+ testing.expect_value(t, s.a[2], 11)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_unix_variadic_limited :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: [dynamic]int `args:"variadic=2"`,
|
|
|
+ b: int,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+
|
|
|
+ args := [?]string { "-a", "11", "101", "-b", "3" }
|
|
|
+
|
|
|
+ result := flags.parse(&s, args[:], .Unix)
|
|
|
+ defer delete(s.a)
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, len(s.a), 2)
|
|
|
+
|
|
|
+ if len(s.a) < 2 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ testing.expect_value(t, s.a[0], 11)
|
|
|
+ testing.expect_value(t, s.a[1], 101)
|
|
|
+ testing.expect_value(t, s.b, 3)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_unix_positional :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ a: int `args:"pos=1"`,
|
|
|
+ b: int `args:"pos=0"`,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+
|
|
|
+ args := [?]string { "-b", "17", "11" }
|
|
|
+
|
|
|
+ result := flags.parse(&s, args[:], .Unix)
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.a, 11)
|
|
|
+ testing.expect_value(t, s.b, 17)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_unix_positional_with_variadic :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ varg: [dynamic]int,
|
|
|
+ v: [dynamic]int `args:"variadic"`,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+
|
|
|
+ args := [?]string { "35", "-v", "17", "11" }
|
|
|
+
|
|
|
+ result := flags.parse(&s, args[:], .Unix)
|
|
|
+ defer {
|
|
|
+ delete(s.varg)
|
|
|
+ delete(s.v)
|
|
|
+ }
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, len(s.varg), 1)
|
|
|
+ testing.expect_value(t, len(s.v), 2)
|
|
|
+}
|
|
|
+
|
|
|
+// This test ensures there are no bad frees with cstrings.
|
|
|
+@(test)
|
|
|
+test_if_dynamic_cstrings_get_freed :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ varg: [dynamic]cstring,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+
|
|
|
+ args := [?]string { "Hellope", "world!" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ defer {
|
|
|
+ for v in s.varg {
|
|
|
+ delete(v)
|
|
|
+ }
|
|
|
+ delete(s.varg)
|
|
|
+ }
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+}
|
|
|
+
|
|
|
+// This test ensures there are no double allocations with cstrings.
|
|
|
+@(test)
|
|
|
+test_if_map_cstrings_get_freed :: proc(t: ^testing.T) {
|
|
|
+ S :: struct {
|
|
|
+ m: map[cstring]cstring,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+
|
|
|
+ args := [?]string { "-m:hellope=world", "-m:hellope=bar", "-m:hellope=foo" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ defer {
|
|
|
+ for _, v in s.m {
|
|
|
+ delete(v)
|
|
|
+ }
|
|
|
+ delete(s.m)
|
|
|
+ }
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.m["hellope"], "foo")
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_os_handle :: proc(t: ^testing.T) {
|
|
|
+ TEMPORARY_FILENAME :: "test_core_flags_write_test_output_data"
|
|
|
+
|
|
|
+ test_data := "Hellope!"
|
|
|
+
|
|
|
+ W :: struct {
|
|
|
+ outf: os.Handle `args:"file=cw"`,
|
|
|
+ }
|
|
|
+ w: W
|
|
|
+
|
|
|
+ args := [?]string { fmt.tprintf("-outf:%s", TEMPORARY_FILENAME) }
|
|
|
+ result := flags.parse(&w, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ if result != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ defer os.close(w.outf)
|
|
|
+ os.write_string(w.outf, test_data)
|
|
|
+
|
|
|
+ R :: struct {
|
|
|
+ inf: os.Handle `args:"file=r"`,
|
|
|
+ }
|
|
|
+ r: R
|
|
|
+
|
|
|
+ args = [?]string { fmt.tprintf("-inf:%s", TEMPORARY_FILENAME) }
|
|
|
+ result = flags.parse(&r, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ if result != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ defer os.close(r.inf)
|
|
|
+ data, read_ok := os.read_entire_file_from_handle(r.inf, context.temp_allocator)
|
|
|
+ testing.expect_value(t, read_ok, true)
|
|
|
+ file_contents_equal := 0 == bytes.compare(transmute([]u8)test_data, data)
|
|
|
+ testing.expectf(t, file_contents_equal, "expected file contents to be the same, got %v", data)
|
|
|
+
|
|
|
+ if file_contents_equal {
|
|
|
+ // Delete the file now that we're done.
|
|
|
+ //
|
|
|
+ // This is not done as a defer or all the time, just in case the file
|
|
|
+ // is useful to debugging.
|
|
|
+ testing.expect_value(t, os.remove(TEMPORARY_FILENAME), os.ERROR_NONE)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_distinct_types :: proc(t: ^testing.T) {
|
|
|
+ I :: distinct int
|
|
|
+ S :: struct {
|
|
|
+ base_i: I `args:"indistinct"`,
|
|
|
+ unmodified_i: I,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+
|
|
|
+ {
|
|
|
+ args := [?]string {"-base-i:1"}
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ args := [?]string {"-unmodified-i:1"}
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ err, ok := result.(flags.Parse_Error)
|
|
|
+ testing.expectf(t, ok, "unexpected result: %v", result)
|
|
|
+ if ok {
|
|
|
+ testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Unsupported_Type)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_datetime :: proc(t: ^testing.T) {
|
|
|
+ when flags.IMPORTING_TIME {
|
|
|
+ W :: struct {
|
|
|
+ t: datetime.DateTime,
|
|
|
+ }
|
|
|
+ w: W
|
|
|
+
|
|
|
+ args := [?]string { "-t:2024-06-04T12:34:56Z" }
|
|
|
+ result := flags.parse(&w, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ if result != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ testing.expect_value(t, w.t.date.year, 2024)
|
|
|
+ testing.expect_value(t, w.t.date.month, 6)
|
|
|
+ testing.expect_value(t, w.t.date.day, 4)
|
|
|
+ } else {
|
|
|
+ log.info("Skipping test due to lack of platform support.")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_net :: proc(t: ^testing.T) {
|
|
|
+ when flags.IMPORTING_NET {
|
|
|
+ W :: struct {
|
|
|
+ addr: net.Host_Or_Endpoint,
|
|
|
+ }
|
|
|
+ w: W
|
|
|
+
|
|
|
+ args := [?]string { "-addr:odin-lang.org:80" }
|
|
|
+ result := flags.parse(&w, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ if result != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ host, is_host := w.addr.(net.Host)
|
|
|
+ testing.expectf(t, is_host, "expected type of `addr` to be `net.Host`, was %v", w.addr)
|
|
|
+ testing.expect_value(t, host.hostname, "odin-lang.org")
|
|
|
+ testing.expect_value(t, host.port, 80)
|
|
|
+ } else {
|
|
|
+ log.info("Skipping test due to lack of platform support.")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_custom_type_setter :: proc(t: ^testing.T) {
|
|
|
+ Custom_Bool :: distinct bool
|
|
|
+ Custom_Data :: struct {
|
|
|
+ a: int,
|
|
|
+ }
|
|
|
+
|
|
|
+ S :: struct {
|
|
|
+ a: Custom_Data,
|
|
|
+ b: Custom_Bool `args:"indistinct"`,
|
|
|
+ }
|
|
|
+ s: S
|
|
|
+
|
|
|
+ // NOTE: Mind that this setter is global state, and the test runner is multi-threaded.
|
|
|
+ // It should be fine so long as all type setter tests are in this one test proc.
|
|
|
+ flags.register_type_setter(proc (data: rawptr, data_type: typeid, _, _: string) -> (string, bool, runtime.Allocator_Error) {
|
|
|
+ if data_type == Custom_Data {
|
|
|
+ (cast(^Custom_Data)data).a = 32
|
|
|
+ return "", true, nil
|
|
|
+ }
|
|
|
+ return "", false, nil
|
|
|
+ })
|
|
|
+ defer flags.register_type_setter(nil)
|
|
|
+ args := [?]string { "-a:hellope", "-b:true" }
|
|
|
+ result := flags.parse(&s, args[:])
|
|
|
+ testing.expect_value(t, result, nil)
|
|
|
+ testing.expect_value(t, s.a.a, 32)
|
|
|
+ testing.expect_value(t, s.b, true)
|
|
|
+}
|
|
|
+
|
|
|
+// This test is sensitive to many of the underlying mechanisms of the library,
|
|
|
+// so if something isn't working, it'll probably show up here first, but it may
|
|
|
+// not be immediately obvious as to what's wrong.
|
|
|
+//
|
|
|
+// It makes for a good early warning system.
|
|
|
+@(test)
|
|
|
+test_usage_write_odin :: proc(t: ^testing.T) {
|
|
|
+ Expected_Output :: `Usage:
|
|
|
+ varg required-number [number] [name] -bars -bots -foos -gadgets -widgets [-array] [-count] [-greek] [-map-type] [-verbose] ...
|
|
|
+Flags:
|
|
|
+ -required-number:<int>, required | some number
|
|
|
+ -number:<int> | some other number
|
|
|
+ -name:<string>
|
|
|
+ Multi-line documentation
|
|
|
+ gets formatted
|
|
|
+ very nicely.
|
|
|
+ -bars:<string>, exactly 3 | <This flag has not been documented yet.>
|
|
|
+ -bots:<string>, at least 1 | <This flag has not been documented yet.>
|
|
|
+ -foos:<string>, between 2 and 3 | <This flag has not been documented yet.>
|
|
|
+ -gadgets:<string>, at least 1 | <This flag has not been documented yet.>
|
|
|
+ -widgets:<string>, at most 2 | <This flag has not been documented yet.>
|
|
|
+ |
|
|
|
+ -array:<rune>, multiple | <This flag has not been documented yet.>
|
|
|
+ -count:<u8> | <This flag has not been documented yet.>
|
|
|
+ -greek:<Custom_Enum> | <This flag has not been documented yet.>
|
|
|
+ -map-type:<cstring>=<u8> | <This flag has not been documented yet.>
|
|
|
+ -verbose | <This flag has not been documented yet.>
|
|
|
+ <string, ...> | <This flag has not been documented yet.>
|
|
|
+`
|
|
|
+
|
|
|
+ Custom_Enum :: enum {
|
|
|
+ Alpha,
|
|
|
+ Omega,
|
|
|
+ }
|
|
|
+
|
|
|
+ S :: struct {
|
|
|
+ required_number: int `args:"pos=0,required" usage:"some number"`,
|
|
|
+ number: int `args:"pos=1" usage:"some other number"`,
|
|
|
+ name: string `args:"pos=2" usage:"
|
|
|
+ Multi-line documentation
|
|
|
+ gets formatted
|
|
|
+very nicely.
|
|
|
+
|
|
|
+"`,
|
|
|
+
|
|
|
+ c: u8 `args:"name=count"`,
|
|
|
+ greek: Custom_Enum,
|
|
|
+
|
|
|
+ array: [dynamic]rune,
|
|
|
+ map_type: map[cstring]byte,
|
|
|
+
|
|
|
+ gadgets: [dynamic]string `args:"required=1"`,
|
|
|
+ widgets: [dynamic]string `args:"required=<3"`,
|
|
|
+ foos: [dynamic]string `args:"required=2<4"`,
|
|
|
+ bars: [dynamic]string `args:"required=3<4"`,
|
|
|
+ bots: [dynamic]string `args:"required"`,
|
|
|
+
|
|
|
+ debug: bool `args:"hidden" usage:"print debug info"`,
|
|
|
+ verbose: bool,
|
|
|
+
|
|
|
+ varg: [dynamic]string,
|
|
|
+ }
|
|
|
+
|
|
|
+ builder := strings.builder_make()
|
|
|
+ defer strings.builder_destroy(&builder)
|
|
|
+ writer := strings.to_stream(&builder)
|
|
|
+ flags.write_usage(writer, S, "varg", .Odin)
|
|
|
+ testing.expect_value(t, strings.to_string(builder), Expected_Output)
|
|
|
+}
|
|
|
+
|
|
|
+@(test)
|
|
|
+test_usage_write_unix :: proc(t: ^testing.T) {
|
|
|
+ Expected_Output :: `Usage:
|
|
|
+ varg required-number [number] [name] --bars --bots --foos --gadgets --variadic-flag --widgets [--array] [--count] [--greek] [--verbose] ...
|
|
|
+Flags:
|
|
|
+ --required-number <int>, required | some number
|
|
|
+ --number <int> | some other number
|
|
|
+ --name <string>
|
|
|
+ Multi-line documentation
|
|
|
+ gets formatted
|
|
|
+ very nicely.
|
|
|
+ --bars <string>, exactly 3 | <This flag has not been documented yet.>
|
|
|
+ --bots <string>, at least 1 | <This flag has not been documented yet.>
|
|
|
+ --foos <string>, between 2 and 3 | <This flag has not been documented yet.>
|
|
|
+ --gadgets <string>, at least 1 | <This flag has not been documented yet.>
|
|
|
+ --variadic-flag <int, ...>, at least 2 | <This flag has not been documented yet.>
|
|
|
+ --widgets <string>, at most 2 | <This flag has not been documented yet.>
|
|
|
+ |
|
|
|
+ --array <rune>, multiple | <This flag has not been documented yet.>
|
|
|
+ --count <u8> | <This flag has not been documented yet.>
|
|
|
+ --greek <Custom_Enum> | <This flag has not been documented yet.>
|
|
|
+ --verbose | <This flag has not been documented yet.>
|
|
|
+ <string, ...> | <This flag has not been documented yet.>
|
|
|
+`
|
|
|
+
|
|
|
+ Custom_Enum :: enum {
|
|
|
+ Alpha,
|
|
|
+ Omega,
|
|
|
+ }
|
|
|
+
|
|
|
+ S :: struct {
|
|
|
+ required_number: int `args:"pos=0,required" usage:"some number"`,
|
|
|
+ number: int `args:"pos=1" usage:"some other number"`,
|
|
|
+ name: string `args:"pos=2" usage:"
|
|
|
+ Multi-line documentation
|
|
|
+ gets formatted
|
|
|
+very nicely.
|
|
|
+
|
|
|
+"`,
|
|
|
+
|
|
|
+ c: u8 `args:"name=count"`,
|
|
|
+ greek: Custom_Enum,
|
|
|
+
|
|
|
+ array: [dynamic]rune,
|
|
|
+ variadic_flag: [dynamic]int `args:"variadic,required=2"`,
|
|
|
+
|
|
|
+ gadgets: [dynamic]string `args:"required=1"`,
|
|
|
+ widgets: [dynamic]string `args:"required=<3"`,
|
|
|
+ foos: [dynamic]string `args:"required=2<4"`,
|
|
|
+ bars: [dynamic]string `args:"required=3<4"`,
|
|
|
+ bots: [dynamic]string `args:"required"`,
|
|
|
+
|
|
|
+ debug: bool `args:"hidden" usage:"print debug info"`,
|
|
|
+ verbose: bool,
|
|
|
+
|
|
|
+ varg: [dynamic]string,
|
|
|
+ }
|
|
|
+
|
|
|
+ builder := strings.builder_make()
|
|
|
+ defer strings.builder_destroy(&builder)
|
|
|
+ writer := strings.to_stream(&builder)
|
|
|
+ flags.write_usage(writer, S, "varg", .Unix)
|
|
|
+ testing.expect_value(t, strings.to_string(builder), Expected_Output)
|
|
|
+}
|