diff options
Diffstat (limited to 'tests')
-rwxr-xr-x | tests/cli-opts.sh | 106 | ||||
-rw-r--r-- | tests/functional/string-round-trip/uuid.go | 23 | ||||
l--------- | tests/fuzz/new-v4-from/main.go (renamed from tests/fuzz/new-from/main.go) | 0 | ||||
-rw-r--r-- | tests/fuzz/new-v4-from/uuid.go (renamed from tests/fuzz/new-from/uuid.go) | 2 | ||||
l--------- | tests/fuzz/new-v7-from/main.go | 1 | ||||
-rw-r--r-- | tests/fuzz/new-v7-from/uuid.go | 33 | ||||
-rw-r--r-- | tests/uuid.go | 387 |
7 files changed, 530 insertions, 22 deletions
diff --git a/tests/cli-opts.sh b/tests/cli-opts.sh index 39f01f4..1d7cab8 100755 --- a/tests/cli-opts.sh +++ b/tests/cli-opts.sh @@ -4,6 +4,85 @@ set -eu . tests/lib.sh +test_unsupported_flags() { + testing 'unsupported flags' + + N="$LINENO" + OUT="$(mkstemp)" + ERR="$(mkstemp)" + trap 'rm -f "$OUT" "$ERR"' EXIT + STATUS=0 + ./uuid.bin -x 1>"$OUT" 2>"$ERR" || STATUS=$? + assert_status 2 + assert_empty_stdout + assert_grep_stderr '^Usage:$' + rm "$OUT" "$ERR" + + test_ok +} + +test_flag_without_required_arguments() { + testing 'flag without required arguments' + + N="$LINENO" + OUT="$(mkstemp)" + ERR="$(mkstemp)" + trap 'rm -f "$OUT" "$ERR"' EXIT + STATUS=0 + ./uuid.bin -v 1>"$OUT" 2>"$ERR" || STATUS=$? + assert_status 2 + assert_empty_stdout + assert_grep_stderr '^Usage:$' + rm "$OUT" "$ERR" + + test_ok +} + +test_flag_with_bad_argument_value() { + testing 'flag without required arguments' + + N="$LINENO" + OUT="$(mkstemp)" + ERR="$(mkstemp)" + trap 'rm -f "$OUT" "$ERR"' EXIT + STATUS=0 + ./uuid.bin -v 1 1>"$OUT" 2>"$ERR" || STATUS=$? + assert_status 2 + assert_empty_stdout + assert_grep_stderr '^Usage:$' + rm "$OUT" "$ERR" + + test_ok +} + +test_generates_uuid_with_custom_version() { + testing 'UUID with custom version' + + N="$LINENO" + OUT="$(mkstemp)" + ERR="$(mkstemp)" + trap 'rm -f "$OUT" "$ERR"' EXIT + STATUS=0 + ./uuid.bin -v 4 1>"$OUT" 2>"$ERR" || STATUS=$? + assert_status 0 + assert_grep_stdout '^.{,14}4' + assert_empty_stderr + rm "$OUT" "$ERR" + + N="$LINENO" + OUT="$(mkstemp)" + ERR="$(mkstemp)" + trap 'rm -f "$OUT" "$ERR"' EXIT + STATUS=0 + ./uuid.bin -v 7 1>"$OUT" 2>"$ERR" || STATUS=$? + assert_status 0 + assert_grep_stdout '^.{,14}7' + assert_empty_stderr + rm "$OUT" "$ERR" + + test_ok +} + test_generates_uuid_with_0_args() { testing 'generates UUID with 0 arguments' @@ -28,7 +107,8 @@ test_checks_string_with_1_arg() { ERR="$(mkstemp)" trap 'rm -f "$OUT" "$ERR"' EXIT STATUS=0 - ./uuid.bin 'cac94e13-41fa-40c4-bd46-5b7b3b46c09e' 1>"$OUT" 2>"$ERR" || STATUS=$? + ID='cac94e13-41fa-40c4-bd46-5b7b3b46c09e' + ./uuid.bin "$ID" 1>"$OUT" 2>"$ERR" || STATUS=$? assert_status 0 assert_empty_stdout assert_empty_stderr @@ -48,7 +128,31 @@ test_checks_string_with_1_arg() { test_ok } +test_checks_string_with_1_arg_ignores_options() { + testing 'checks string with 1 arg ignores options' + + N="$LINENO" + OUT="$(mkstemp)" + ERR="$(mkstemp)" + trap 'rm -f "$OUT" "$ERR"' EXIT + STATUS=0 + ID='cac94e13-41fa-40c4-bd46-5b7b3b46c09e' + ID='76600ea2-0282-4b38-9bc9-dd69125445f3' + ./uuid.bin -v 4 "$ID" 1>"$OUT" 2>"$ERR" || STATUS=$? + assert_status 0 + assert_empty_stdout + assert_empty_stderr + rm "$OUT" "$ERR" + + test_ok +} + +test_unsupported_flags +test_flag_without_required_arguments test_generates_uuid_with_0_args +test_flag_with_bad_argument_value +test_generates_uuid_with_custom_version test_checks_string_with_1_arg +test_checks_string_with_1_arg_ignores_options diff --git a/tests/functional/string-round-trip/uuid.go b/tests/functional/string-round-trip/uuid.go index 000d3fb..82414ea 100644 --- a/tests/functional/string-round-trip/uuid.go +++ b/tests/functional/string-round-trip/uuid.go @@ -45,16 +45,31 @@ func assertEq(given any, expected any) { func MainTest() { - testing("string is the same after round-trip", func() { - str1 := New().String() + testing("v4 string is the same after round-trip", func() { + str1 := NewV4().String() id, err := FromString(str1) assertEq(err, nil) str2 := id.String() assertEq(str1, str2) }) - testing("UUID is the same after round-trip", func() { - id1 := New() + testing("v4 UUID is the same after round-trip", func() { + id1 := NewV4() + id2, err := FromString(id1.String()) + assertEq(err, nil) + assertEq(id1, id2) + }) + + testing("v7 string is the same after round-trip", func() { + str1 := NewV7().String() + id, err := FromString(str1) + assertEq(err, nil) + str2 := id.String() + assertEq(str1, str2) + }) + + testing("v7 UUID is the same after round-trip", func() { + id1 := NewV7() id2, err := FromString(id1.String()) assertEq(err, nil) assertEq(id1, id2) diff --git a/tests/fuzz/new-from/main.go b/tests/fuzz/new-v4-from/main.go index f67563d..f67563d 120000 --- a/tests/fuzz/new-from/main.go +++ b/tests/fuzz/new-v4-from/main.go diff --git a/tests/fuzz/new-from/uuid.go b/tests/fuzz/new-v4-from/uuid.go index 298687b..90cc994 100644 --- a/tests/fuzz/new-from/uuid.go +++ b/tests/fuzz/new-v4-from/uuid.go @@ -11,7 +11,7 @@ import ( func fn(f *testing.F) { f.Fuzz(func(t *testing.T, payload []byte) { - NewFrom(bytes.NewReader(payload)) + NewV4From(bytes.NewReader(payload)) }) } diff --git a/tests/fuzz/new-v7-from/main.go b/tests/fuzz/new-v7-from/main.go new file mode 120000 index 0000000..f67563d --- /dev/null +++ b/tests/fuzz/new-v7-from/main.go @@ -0,0 +1 @@ +../../main.go
\ No newline at end of file diff --git a/tests/fuzz/new-v7-from/uuid.go b/tests/fuzz/new-v7-from/uuid.go new file mode 100644 index 0000000..8686ebf --- /dev/null +++ b/tests/fuzz/new-v7-from/uuid.go @@ -0,0 +1,33 @@ +package uuid + +import ( + "bytes" + "os" + "testing" + "testing/internal/testdeps" +) + + + +func fn(f *testing.F) { + f.Fuzz(func(t *testing.T, payload []byte, nanosecs uint64) { + NewV7From(bytes.NewReader(payload), func() uint64 { + return nanosecs + }) + }) +} + + + +func MainTest() { + fuzzTargets := []testing.InternalFuzzTarget{ + { "fn", fn }, + } + + deps := testdeps.TestDeps{} + tests := []testing.InternalTest {} + benchmarks := []testing.InternalBenchmark{} + examples := []testing.InternalExample {} + m := testing.MainStart(deps, tests, benchmarks, fuzzTargets, examples) + os.Exit(m.Run()) +} diff --git a/tests/uuid.go b/tests/uuid.go index ad9cac4..912339a 100644 --- a/tests/uuid.go +++ b/tests/uuid.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/hex" "fmt" + "io" "os" "reflect" "strings" @@ -50,18 +51,24 @@ func assertEq(given any, expected any) { } -func test_NewFrom() { - testStart("NewFrom()") + +func test_NewV4From() { + testStart("NewV4From()") + + testing("we propagate the error when it happens", func() { + _, err := NewV4From(strings.NewReader("")) + assertEq(err, io.EOF) + }) testing("we get the same UUID from the same input", func() { const s = "abcdefghijklmnop" r1 := strings.NewReader(s) - uuid1, err := NewFrom(r1) + uuid1, err := NewV4From(r1) assertEq(err, nil) r2 := strings.NewReader(s) - uuid2, err := NewFrom(r2) + uuid2, err := NewV4From(r2) assertEq(err, nil) assertEq(uuid1, uuid2) @@ -107,7 +114,7 @@ func test_NewFrom() { } r := bytes.NewReader(input) - given, err := NewFrom(r) + given, err := NewV4From(r) assertEq(err, nil) assertEq(given, expected) }) @@ -152,30 +159,166 @@ func test_NewFrom() { } r := bytes.NewReader(input) - given, err := NewFrom(r) + given, err := NewV4From(r) assertEq(err, nil) assertEq(given, expected) }) } -func test_New() { - testStart("New()") +func test_NewV4() { + testStart("NewV4()") testing("we can generate UUID values: ", func() { - var uuid UUID = New() - assertEq(len(uuid), 16) + var uuid UUID = NewV4() + assertEq(uuid == Nil, false) + assertEq(uuid == Max, false) }) testing("panic when the randomReader fails", func() { savedReader := randomReader - randomReader = strings.NewReader("abc") + randomReader = strings.NewReader("") + defer func() { + r := recover() + assertEq(r, io.EOF) + randomReader = savedReader + }() + + NewV4() + os.Exit(5) + }) +} + +func test_NewV7From() { + testStart("NewV7From()") + + testing("reader error is propagated", func() { + nowFn := func() uint64 { + return 0 + } + + _, err := NewV7From(strings.NewReader(""), nowFn) + assertEq(err, io.EOF) + }) + + testing("we get the same UUID given the same input", func() { + const s = "abcdefgh" + nowFn := func() uint64 { + return 0 + } + + r1 := strings.NewReader(s) + uuid1, err := NewV7From(r1, nowFn) + assertEq(err, nil) + + r2 := strings.NewReader(s) + uuid2, err := NewV7From(r2, nowFn) + assertEq(err, nil) + + assertEq(uuid1, uuid2) + }) + + testing("the bytes are what the reader plus the time gives", func() { + randomInput := []byte{ + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + } + + nowFn := func() uint64 { + return 0xffffffff00000000 + } + + expected := UUID{ + 0xff, + 0xff, + 0xff, + 0xff, + 0x00, + 0x00, + 0x00 + 0x70, + 0x00, + 0x00 + 0x80, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + } + + r := bytes.NewReader(randomInput) + given, err := NewV7From(r, nowFn) + assertEq(err, nil) + assertEq(given, expected) + }) + + testing("v7 and variant markers", func() { + randomInput := []byte{ + 0x11, + 0x11, + 0x11, + 0x11, + 0x11, + 0x11, + 0x11, + 0x11, + } + + nowFn := func() uint64 { + return 0x1111111111111111 + } + + expected := UUID{ + 0x11, + 0x11, + 0x11, + 0x11, + 0x11, + 0x11, + 0x71, // not 0x11 + 0x11, + 0x91, // not 0x11 + 0x11, + 0x11, + 0x11, + 0x11, + 0x11, + 0x11, + 0x11, + } + + r := bytes.NewReader(randomInput) + given, err := NewV7From(r, nowFn) + assertEq(err, nil) + assertEq(given, expected) + }) +} + +func test_NewV7() { + testStart("NewV7()") + + testing("we can generate UUID values: ", func() { + var uuid UUID = NewV7() + assertEq(uuid == Nil, false) + assertEq(uuid == Max, false) + }) + + testing("panic when reader fails", func() { + savedReader := randomReader + randomReader = strings.NewReader("") defer func() { r := recover() - assertEq(r == nil, false) + assertEq(r, io.EOF) randomReader = savedReader }() - New() + NewV7() os.Exit(5) }) } @@ -224,7 +367,7 @@ func test_FromString() { testing("UUID -> string -> UUID round trip", func() { for i := 0; i < 100; i++ { - uuid0 := New() + uuid0 := NewV4() uuid1, err := FromString(uuid0.String()) assertEq(err, nil) assertEq(uuid0, uuid1) @@ -251,11 +394,223 @@ func test_FromString() { }) } +func test_usage() { + testStart("usage()") + + testing("all it does is write to the given io.Writer", func() { + w := strings.Builder{} + usage("xxx", &w) + + const expectedRaw = ` + Usage: + xxx [-v (4|7)] + xxx STRING + ` + expected := strings.Trim( + strings.ReplaceAll(expectedRaw, "\t", ""), + "\n", + ) + "\n" + assertEq(w.String(), expected) + }) +} + +func test_actionForSubargs() { + testStart("actionForSubargs()") + + testing("we decide based only on length", func() { + assertEq(actionForSubargs([]string{}), actionType_generate) + assertEq(actionForSubargs([]string{""}), actionType_parse) + assertEq(actionForSubargs([]string{"id"}), actionType_parse) + }) +} + +func test_getopt() { + testStart("getopt()") + + const usageRaw = ` + Usage: + $0 [-v (4|7)] + $0 STRING + ` + usage := strings.Trim( + strings.ReplaceAll(usageRaw, "\t", ""), + "\n", + ) + "\n" + + testing("we supress the default error message", func() { + w := strings.Builder{} + argv := []string{"$0", "-h"} + _, rc := getopt(argv, &w) + + assertEq(w.String(), usage) + assertEq(rc, 2) + }) + + testing("we get unsupported flag error", func() { + w := strings.Builder{} + argv := []string{"$0", "-Z"} + _, rc := getopt(argv, &w) + + const message = "flag provided but not defined: -Z\n" + assertEq(w.String(), message + usage) + assertEq(rc, 2) + }) + + testing("we get incorrect use of flag error", func() { + w := strings.Builder{} + argv := []string{"$0", "-v"} + _, rc := getopt(argv, &w) + + const message = "flag needs an argument: -v\n" + assertEq(w.String(), message + usage) + assertEq(rc, 2) + }) + + testing("we get bad flag value error", func() { + w := strings.Builder{} + argv := []string{"$0", "-v", "a"} + _, rc := getopt(argv, &w) + + const message = "invalid value \"a\" for flag -v: parse error\n" + assertEq(w.String(), message + usage) + assertEq(rc, 2) + }) + + testing("the args has the picked version", func() { + var ( + w1 = strings.Builder{} + w2 = strings.Builder{} + w3 = strings.Builder{} + ) + + argsIn1 := []string{"$0"} + argsIn2 := []string{"$0", "-v", "4"} + argsIn3 := []string{"$0", "-v", "7"} + + args1, rc1 := getopt(argsIn1, &w1) + args2, rc2 := getopt(argsIn2, &w2) + args3, rc3 := getopt(argsIn3, &w3) + + expected1 := argsT{ + allArgs: []string{"$0"}, + subArgs: []string{}, + action: actionType_generate, + version: 4, + } + expected2 := argsT{ + allArgs: []string{"$0", "-v", "4"}, + subArgs: []string{}, + action: actionType_generate, + version: 4, + } + expected3 := argsT{ + allArgs: []string{"$0", "-v", "7"}, + subArgs: []string{}, + action: actionType_generate, + version: 7, + } + + assertEq(w1.String(), "") + assertEq(w2.String(), "") + assertEq(w3.String(), "") + assertEq(rc1, 0) + assertEq(rc2, 0) + assertEq(rc3, 0) + assertEq(args1, expected1) + assertEq(args2, expected2) + assertEq(args3, expected3) + }) + + testing("the args has the picked action", func() { + var ( + w1 = strings.Builder{} + w2 = strings.Builder{} + ) + + argsIn1 := []string{"$0", "the-string"} + argsIn2 := []string{"$0", "-v", "7", "the-string"} + + args1, rc1 := getopt(argsIn1, &w1) + args2, rc2 := getopt(argsIn2, &w2) + + expected1 := argsT{ + allArgs: []string{"$0", "the-string"}, + subArgs: []string{"the-string"}, + action: actionType_parse, + version: 4, + } + expected2 := argsT{ + allArgs: []string{"$0", "-v", "7", "the-string"}, + subArgs: []string{"the-string"}, + action: actionType_parse, + version: 7, + } + + assertEq(w1.String(), "") + assertEq(w2.String(), "") + assertEq(rc1, 0) + assertEq(rc2, 0) + assertEq(args1, expected1) + assertEq(args2, expected2) + }) +} + +func test_run() { + testStart("run()") + + testing("generating IDs gives us 0 rc", func() { + out1 := strings.Builder{} + out2 := strings.Builder{} + args1 := argsT{ + action: actionType_generate, + version: 4, + } + args2 := argsT{ + action: actionType_generate, + version: 7, + } + + rc1 := run(args1, nil, &out1, nil) + rc2 := run(args2, nil, &out2, nil) + + assertEq(rc1, 0) + assertEq(rc2, 0) + assertEq(len(out1.String()), encodedLength + len("\n")) + assertEq(len(out2.String()), encodedLength + len("\n")) + }) + + testing("parsing varies based on ID validity", func() { + const id = "ggggggg-ggggg-gggg-gggg-gggggggggggg" + err1 := strings.Builder{} + args1 := argsT{ + action: actionType_parse, + subArgs: []string{ id }, + } + args2 := argsT{ + action: actionType_parse, + subArgs: []string{New().String()}, + } + + rc1 := run(args1, nil, nil, &err1) + rc2 := run(args2, nil, nil, nil) + + assertEq(rc1, 3) + assertEq(rc2, 0) + assertEq(err1.String(), "uuid: Bad char in string\n") + }) +} + func MainTest() { - test_NewFrom() - test_New() + test_NewV4From() + test_NewV4() + test_NewV7From() + test_NewV7() test_String() test_FromString() + test_usage() + test_actionForSubargs() + test_getopt() + test_run() } |