package text import ( "bytes" "testing" "lukechampine.com/frand" "realy.lol/chk" "realy.lol/hex" "realy.lol/sha256" ) func TestUnmarshalHexArray(t *testing.T) { var ha [][]byte h := make([]byte, sha256.Size) frand.Read(h) var dst []byte for _ = range 20 { hh := sha256.Sum256(h) h = hh[:] ha = append(ha, h) } dst = append(dst, '[') for i := range ha { dst = AppendQuote(dst, ha[i], hex.EncAppend) if i != len(ha)-1 { dst = append(dst, ',') } } dst = append(dst, ']') var ha2 [][]byte var rem []byte var err error if ha2, rem, err = UnmarshalHexArray(dst, 32); chk.E(err) { t.Fatal(err) } if len(ha2) != len(ha) { t.Fatalf("failed to unmarshal, got %d fields, expected %d", len(ha2), len(ha)) } if len(rem) > 0 { t.Fatalf("failed to unmarshal, remnant afterwards '%s'", rem) } for i := range ha2 { if !bytes.Equal(ha[i], ha2[i]) { t.Fatalf("failed to unmarshal at element %d; got %x, expected %x", i, ha[i], ha2[i]) } } } func TestJSONKey(t *testing.T) { tests := []struct { name string dst []byte key []byte expected []byte }{ { name: "basic key", dst: []byte("prefix"), key: []byte("name"), expected: []byte(`prefix"name":`), }, { name: "empty dst", dst: []byte{}, key: []byte("id"), expected: []byte(`"id":`), }, { name: "empty key", dst: []byte("start"), key: []byte{}, expected: []byte(`start"":`), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := JSONKey(tt.dst, tt.key) if !bytes.Equal(result, tt.expected) { t.Errorf("JSONKey() = %q, want %q", result, tt.expected) } }) } } func TestUnmarshalHex(t *testing.T) { tests := []struct { name string input []byte expectedHex []byte expectedRem []byte wantErr bool }{ { name: "basic hex", input: []byte(`"01234567"remaining`), expectedHex: []byte{0x01, 0x23, 0x45, 0x67}, expectedRem: []byte("remaining"), wantErr: false, }, { name: "empty hex", input: []byte(`""rest`), expectedHex: []byte{}, expectedRem: []byte("rest"), wantErr: false, }, { name: "no quotes", input: []byte("01234567"), expectedHex: nil, expectedRem: []byte("01234567"), wantErr: true, }, { name: "odd length hex", input: []byte(`"123"`), expectedHex: nil, expectedRem: []byte{}, wantErr: true, }, { name: "invalid hex characters", input: []byte(`"gg"`), expectedHex: nil, expectedRem: []byte{}, wantErr: true, }, { name: "empty input", input: []byte{}, expectedHex: nil, expectedRem: []byte{}, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hex, rem, err := UnmarshalHex(tt.input) if (err != nil) != tt.wantErr { t.Errorf("UnmarshalHex() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr { if !bytes.Equal(hex, tt.expectedHex) { t.Errorf("UnmarshalHex() hex = %v, want %v", hex, tt.expectedHex) } if !bytes.Equal(rem, tt.expectedRem) { t.Errorf("UnmarshalHex() rem = %q, want %q", rem, tt.expectedRem) } } }) } } func TestUnmarshalQuoted(t *testing.T) { tests := []struct { name string input []byte expectedContent []byte expectedRem []byte wantErr bool }{ { name: "basic quoted string", input: []byte(`"hello"world`), expectedContent: []byte("hello"), expectedRem: []byte("world"), wantErr: false, }, { name: "empty quoted string", input: []byte(`""rest`), expectedContent: []byte{}, expectedRem: []byte("rest"), wantErr: false, }, { name: "escaped quotes", input: []byte(`"say \"hello\""end`), expectedContent: []byte(`say "hello"`), expectedRem: []byte("end"), wantErr: false, }, { name: "escaped backslash", input: []byte(`"path\\to\\file"rest`), expectedContent: []byte(`path\to\file`), expectedRem: []byte("rest"), wantErr: false, }, { name: "no opening quote", input: []byte(`hello"world`), expectedContent: []byte("world"), expectedRem: []byte{}, wantErr: false, }, { name: "no closing quote", input: []byte(`"hello`), expectedContent: []byte("hello"), expectedRem: []byte{}, wantErr: false, }, { name: "empty input", input: []byte{}, expectedContent: nil, expectedRem: []byte{}, wantErr: true, }, { name: "invalid control character", input: []byte("\"hello\tworld\""), expectedContent: nil, expectedRem: []byte{}, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { content, rem, err := UnmarshalQuoted(tt.input) if (err != nil) != tt.wantErr { t.Errorf("UnmarshalQuoted() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr { if !bytes.Equal(content, tt.expectedContent) { t.Errorf("UnmarshalQuoted() content = %q, want %q", content, tt.expectedContent) } if !bytes.Equal(rem, tt.expectedRem) { t.Errorf("UnmarshalQuoted() rem = %q, want %q", rem, tt.expectedRem) } } }) } } func TestMarshalHexArray(t *testing.T) { tests := []struct { name string dst []byte ha [][]byte expected []byte }{ { name: "basic hex array", dst: []byte("prefix"), ha: [][]byte{{0x01, 0x23}, {0x45, 0x67}}, expected: []byte(`prefix["0123","4567"]`), }, { name: "empty array", dst: []byte("start"), ha: [][]byte{}, expected: []byte("start[]"), }, { name: "single element", dst: []byte{}, ha: [][]byte{{0xff}}, expected: []byte(`["ff"]`), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := MarshalHexArray(tt.dst, tt.ha) if !bytes.Equal(result, tt.expected) { t.Errorf("MarshalHexArray() = %q, want %q", result, tt.expected) } }) } } func TestUnmarshalStringArray(t *testing.T) { tests := []struct { name string input []byte expectedArr [][]byte expectedRem []byte wantErr bool }{ { name: "basic string array", input: []byte(`["hello","world"]rest`), expectedArr: [][]byte{[]byte("hello"), []byte("world")}, expectedRem: []byte("rest"), wantErr: false, }, { name: "empty array", input: []byte(`[]remaining`), expectedArr: [][]byte{}, expectedRem: []byte("remaining"), wantErr: false, }, { name: "single element", input: []byte(`["single"]end`), expectedArr: [][]byte{[]byte("single")}, expectedRem: []byte("end"), wantErr: false, }, { name: "no opening bracket", input: []byte(`"hello","world"]`), expectedArr: [][]byte{}, expectedRem: []byte{}, wantErr: false, }, { name: "no closing bracket", input: []byte(`["hello","world"`), expectedArr: [][]byte{[]byte("hello"), []byte("world")}, expectedRem: []byte{}, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { arr, rem, err := UnmarshalStringArray(tt.input) if (err != nil) != tt.wantErr { t.Errorf("UnmarshalStringArray() error = %v, wantErr %v", err, tt.wantErr) return } if len(arr) != len(tt.expectedArr) { t.Errorf("UnmarshalStringArray() array length = %d, want %d", len(arr), len(tt.expectedArr)) return } for i := range arr { if !bytes.Equal(arr[i], tt.expectedArr[i]) { t.Errorf("UnmarshalStringArray() arr[%d] = %q, want %q", i, arr[i], tt.expectedArr[i]) } } if !bytes.Equal(rem, tt.expectedRem) { t.Errorf("UnmarshalStringArray() rem = %q, want %q", rem, tt.expectedRem) } }) } } func TestTrue(t *testing.T) { result := True() expected := []byte("true") if !bytes.Equal(result, expected) { t.Errorf("True() = %q, want %q", result, expected) } } func TestFalse(t *testing.T) { result := False() expected := []byte("false") if !bytes.Equal(result, expected) { t.Errorf("False() = %q, want %q", result, expected) } } func TestMarshalBool(t *testing.T) { tests := []struct { name string src []byte truth bool expected []byte }{ { name: "true value", src: []byte("prefix"), truth: true, expected: []byte("prefixtrue"), }, { name: "false value", src: []byte("prefix"), truth: false, expected: []byte("prefixfalse"), }, { name: "true with empty src", src: []byte{}, truth: true, expected: []byte("true"), }, { name: "false with empty src", src: []byte{}, truth: false, expected: []byte("false"), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := MarshalBool(tt.src, tt.truth) if !bytes.Equal(result, tt.expected) { t.Errorf("MarshalBool() = %q, want %q", result, tt.expected) } }) } } func TestUnmarshalBool(t *testing.T) { tests := []struct { name string input []byte expectedRem []byte expectedTruth bool wantErr bool }{ { name: "true value", input: []byte("truerest"), expectedRem: []byte("rest"), expectedTruth: true, wantErr: false, }, { name: "false value", input: []byte("falserest"), expectedRem: []byte("rest"), expectedTruth: false, wantErr: false, }, { name: "true at end", input: []byte("true"), expectedRem: []byte{}, expectedTruth: true, wantErr: false, }, { name: "false at end", input: []byte("false"), expectedRem: []byte{}, expectedTruth: false, wantErr: false, }, { name: "true with prefix", input: []byte("prefixtruerest"), expectedRem: []byte("rest"), expectedTruth: true, wantErr: false, }, { name: "false with prefix", input: []byte("prefixfalserest"), expectedRem: []byte("rest"), expectedTruth: false, wantErr: false, }, { name: "no boolean value", input: []byte("nothing"), expectedRem: []byte{}, expectedTruth: false, wantErr: true, }, { name: "empty input", input: []byte{}, expectedRem: []byte{}, expectedTruth: false, wantErr: true, }, { name: "incomplete true", input: []byte("tr"), expectedRem: []byte{}, expectedTruth: false, wantErr: true, }, { name: "incomplete false", input: []byte("fal"), expectedRem: []byte{}, expectedTruth: false, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { rem, truth, err := UnmarshalBool(tt.input) if (err != nil) != tt.wantErr { t.Errorf("UnmarshalBool() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr { if truth != tt.expectedTruth { t.Errorf("UnmarshalBool() truth = %v, want %v", truth, tt.expectedTruth) } if !bytes.Equal(rem, tt.expectedRem) { t.Errorf("UnmarshalBool() rem = %q, want %q", rem, tt.expectedRem) } } }) } } func TestComma(t *testing.T) { tests := []struct { name string input []byte expectedRem []byte wantErr bool }{ { name: "comma found", input: []byte("prefix,rest"), expectedRem: []byte(",rest"), wantErr: false, }, { name: "comma at start", input: []byte(",rest"), expectedRem: []byte(",rest"), wantErr: false, }, { name: "no comma", input: []byte("nocomma"), expectedRem: []byte("nocomma"), wantErr: true, }, { name: "empty input", input: []byte{}, expectedRem: []byte{}, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { rem, err := Comma(tt.input) if (err != nil) != tt.wantErr { t.Errorf("Comma() error = %v, wantErr %v", err, tt.wantErr) return } if !bytes.Equal(rem, tt.expectedRem) { t.Errorf("Comma() rem = %q, want %q", rem, tt.expectedRem) } }) } } // Additional tests to reach 100% coverage func TestUnmarshalQuotedEOF(t *testing.T) { // Test EOF error case - when rem becomes empty during processing content, rem, err := UnmarshalQuoted([]byte(`"`)) if err == nil { t.Error("UnmarshalQuoted should return EOF error for incomplete quoted string") } // The function returns empty slice, not nil if len(content) != 0 { t.Errorf("Expected empty content, got %v", content) } if len(rem) != 0 { t.Errorf("Expected empty rem, got %v", rem) } } func TestUnmarshalHexArrayEdgeCases(t *testing.T) { // Test closing bracket case - this should return error for wrong size _, _, err := UnmarshalHexArray([]byte(`["]`), 1) if err == nil { t.Error("UnmarshalHexArray should return error for wrong hex size") } // Test invalid hex size error _, _, err2 := UnmarshalHexArray([]byte(`["01"]`), 2) if err2 == nil { t.Error("UnmarshalHexArray should return error for wrong hex size") } // Test invalid hex content error _, _, err3 := UnmarshalHexArray([]byte(`["gg"]`), 1) if err3 == nil { t.Error("UnmarshalHexArray should return error for invalid hex") } // Test other character handling arr4, rem4, err4 := UnmarshalHexArray([]byte(`[x]`), 1) if err4 != nil { t.Errorf("UnmarshalHexArray should handle other characters, got error: %v", err4) } if len(arr4) != 0 { t.Errorf("Expected empty array, got %v", arr4) } if !bytes.Equal(rem4, []byte{}) { t.Errorf("Expected empty rem, got %v", rem4) } // Test non-bracket start arr5, rem5, err5 := UnmarshalHexArray([]byte(`x]`), 1) if err5 != nil { t.Errorf("UnmarshalHexArray should handle non-bracket start, got error: %v", err5) } if len(arr5) != 0 { t.Errorf("Expected empty array, got %v", arr5) } if !bytes.Equal(rem5, []byte{}) { t.Errorf("Expected empty rem, got %v", rem5) } // Test closing bracket without quote arr6, rem6, err6 := UnmarshalHexArray([]byte(`[]`), 1) if err6 != nil { t.Errorf("UnmarshalHexArray should handle empty array, got error: %v", err6) } if len(arr6) != 0 { t.Errorf("Expected empty array, got %v", arr6) } if !bytes.Equal(rem6, []byte{}) { t.Errorf("Expected empty rem, got %v", rem6) } } func TestUnmarshalStringArrayEdgeCases(t *testing.T) { // Test UnmarshalQuoted error case - this actually doesn't fail, it just processes what it can arr, _, err := UnmarshalStringArray([]byte(`["hello`)) if err != nil { t.Errorf("UnmarshalStringArray should handle incomplete string, got error: %v", err) } // It should extract "hello" even without closing quote if len(arr) != 1 || !bytes.Equal(arr[0], []byte("hello")) { t.Errorf("Expected [hello], got %v", arr) } // Test other character handling arr2, rem2, err2 := UnmarshalStringArray([]byte(`[x]`)) if err2 != nil { t.Errorf("UnmarshalStringArray should handle other characters, got error: %v", err2) } if len(arr2) != 0 { t.Errorf("Expected empty array, got %v", arr2) } if !bytes.Equal(rem2, []byte{}) { t.Errorf("Expected empty rem, got %v", rem2) } // Test case that actually triggers UnmarshalQuoted error - control character // This test covers the error path in UnmarshalStringArray when UnmarshalQuoted fails _, _, err3 := UnmarshalStringArray([]byte(`["hello` + string(rune(9)) + `world"]`)) if err3 == nil { t.Error("UnmarshalStringArray should return error for control characters") } }