123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769 |
- /*
- * EliasDB
- *
- * Copyright 2016 Matthias Ladkau. All rights reserved.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
- package util
- import (
- "fmt"
- "strings"
- "testing"
- "devt.de/krotik/common/bitutil"
- "devt.de/krotik/eliasdb/hash"
- "devt.de/krotik/eliasdb/storage"
- )
- func TestIndexManager(t *testing.T) {
- sm := storage.NewMemoryStorageManager("testsm")
- htree, _ := hash.NewHTree(sm)
- im := NewIndexManager(htree)
- obj1 := make(map[string]string)
- obj1["aaa"] = "DDD voldaaa ddd"
- obj1["bbb"] = "vbbb"
- im.Index("testkey", obj1)
- if res, _ := im.LookupWord("aaa", "ddd"); fmt.Sprint(res) != "map[testkey:[1 3]]" {
- t.Error("Unexpected lookup result:", res)
- return
- }
- CaseSensitiveWordIndex = true
- sm = storage.NewMemoryStorageManager("testsm")
- htree, _ = hash.NewHTree(sm)
- im = NewIndexManager(htree)
- im.Index("testkey", obj1)
- if res, _ := im.LookupWord("aaa", "ddd"); fmt.Sprint(res) != "map[testkey:[3]]" {
- t.Error("Unexpected lookup result:", res)
- return
- }
- if res, _ := im.Count("aaa", "ddd"); res != 1 {
- t.Error("Unexpected count result:", res)
- return
- }
- CaseSensitiveWordIndex = false
- sm = storage.NewMemoryStorageManager("testsm")
- htree, _ = hash.NewHTree(sm)
- im = NewIndexManager(htree)
- im.Index("testkey", obj1)
- if res, _ := im.LookupWord("aaa", "ddd"); fmt.Sprint(res) != "map[testkey:[1 3]]" {
- t.Error("Unexpected lookup result:", res)
- return
- }
- for i := 0; i < 6; i++ {
- sm.AccessMap[uint64(i)] = storage.AccessCacheAndFetchError
- }
- if _, err := im.LookupWord("aaa", "ddd"); !strings.Contains(err.Error(), "Slot not found") {
- t.Error("Unexpected lookup result:", err)
- return
- }
- if err := im.Index("testkey", obj1); !strings.Contains(err.Error(), "Slot not found") {
- t.Error("Unexpected index result:", err)
- return
- }
- if err := im.Deindex("testkey", obj1); !strings.Contains(err.Error(), "Slot not found") {
- t.Error("Unexpected index result:", err)
- return
- }
- for i := 0; i < 6; i++ {
- delete(sm.AccessMap, uint64(i))
- }
- obj2 := make(map[string]string)
- obj2["aaa"] = "ddd vnewaaa"
- obj2["ccc"] = "ccc"
- if err := im.Reindex("testkey", obj2, obj1); err != nil {
- t.Error(err)
- return
- }
- if res := countChildren(htree); res != 5 {
- t.Error("Unexpected number of children:", res)
- return
- }
- if res, err := im.LookupWord("aaa", "DdD"); fmt.Sprint(res) != "map[testkey:[1]]" {
- t.Error("Unexpected lookup result:", res, err)
- return
- }
- if res, _ := im.Count("aaa", "dDD"); res != 1 {
- t.Error("Unexpected count result:", res)
- return
- }
- if res, _ := im.Count("aab", "dDD"); res != 0 {
- t.Error("Unexpected count result:", res)
- return
- }
- if err := im.Deindex("testkey", obj2); err != nil {
- t.Error(err)
- return
- }
- if res := countChildren(htree); res != 0 {
- t.Error("Unexpected number of children:", res)
- return
- }
- }
- func TestPhraseSearch(t *testing.T) {
- sm := storage.NewMemoryStorageManager("testsm")
- htree, _ := hash.NewHTree(sm)
- im := NewIndexManager(htree)
- obj1 := make(map[string]string)
- obj1["aaa"] = "the Is xxx The grass Is grEEn zzz"
- im.Index("testkey", obj1)
- obj2 := make(map[string]string)
- obj2["aaa"] = "the Is xxx The grass Is graY zzz"
- im.Index("testkey2", obj2)
- obj3 := make(map[string]string)
- obj3["aaa"] = "green grass is green zzz"
- im.Index("testkey3", obj3)
- obj4 := make(map[string]string)
- obj4["aaa"] = "green grass is so green zzz"
- obj4["bbb"] = "test"
- im.Index("testkey4", obj4)
- res, err := im.LookupPhrase("aaa", "grass is green")
- if fmt.Sprint(res) != "[testkey testkey3]" || err != nil {
- t.Error("Unexpected lookup result:", res, err)
- }
- res, err = im.LookupPhrase("aaa", "zzz")
- if fmt.Sprint(res) != "[testkey testkey2 testkey3 testkey4]" || err != nil {
- t.Error("Unexpected lookup result:", res, err)
- }
- res, err = im.LookupPhrase("bbb", "test")
- if fmt.Sprint(res) != "[testkey4]" || err != nil {
- t.Error("Unexpected lookup result:", res, err)
- }
- // Test empty return cases
- res, err = im.LookupPhrase("bbb", "")
- if res != nil || err != nil {
- t.Error("Unexpected result:", res, err)
- }
- res, err = im.LookupPhrase("bbb", "b")
- if res != nil || err != nil {
- t.Error("Unexpected result:", res, err)
- }
- for i := 0; i < 10; i++ {
- sm.AccessMap[uint64(i)] = storage.AccessCacheAndFetchError
- }
- res, err = im.LookupPhrase("aaa", "grass is green")
- if res != nil || !strings.Contains(err.Error(), "Slot not found") {
- t.Error("Unexpected result:", res, err)
- }
- if _, err := im.Count("aaa", "grass"); !strings.Contains(err.Error(), "Slot not found") {
- t.Error("Unexpected count result:", err)
- return
- }
- for i := 0; i < 10; i++ {
- delete(sm.AccessMap, uint64(i))
- }
- }
- func TestUpdateIndex(t *testing.T) {
- sm := storage.NewMemoryStorageManager("testsm")
- htree, _ := hash.NewHTree(sm)
- im := NewIndexManager(htree)
- obj1 := make(map[string]string)
- obj1["aaa"] = "vnewaaa ddd"
- obj1["bbb"] = "vbbb"
- obj2 := make(map[string]string)
- obj2["aaa"] = "ddd voldaaa"
- obj2["ccc"] = "ccc"
- // Insert into the index
- im.updateIndex("123", obj2, nil)
- // Check that the entries exist
- entry, _ := htree.Get([]byte(PrefixAttrWord + "aaa" + "ddd"))
- pos := entry.(*indexEntry).WordPos["123"]
- if fmt.Sprint(bitutil.UnpackList(pos)) != "[1]" {
- t.Error("Unexpected result:", fmt.Sprint(bitutil.UnpackList(pos)))
- return
- }
- entry, _ = htree.Get([]byte(PrefixAttrWord + "aaa" + "voldaaa"))
- pos = entry.(*indexEntry).WordPos["123"]
- if fmt.Sprint(bitutil.UnpackList(pos)) != "[2]" {
- t.Error("Unexpected result:", fmt.Sprint(bitutil.UnpackList(pos)))
- return
- }
- entry, _ = htree.Get([]byte(PrefixAttrWord + "ccc" + "ccc"))
- pos = entry.(*indexEntry).WordPos["123"]
- if fmt.Sprint(bitutil.UnpackList(pos)) != "[1]" {
- t.Error("Unexpected result:", fmt.Sprint(bitutil.UnpackList(pos)))
- return
- }
- if res := countChildren(htree); res != 5 {
- t.Error("Unexpected number of children:", res)
- return
- }
- // Update the index
- im.updateIndex("123", obj1, obj2)
- if res := countChildren(htree); res != 5 {
- t.Error("Unexpected number of children:", res)
- return
- }
- // Check that the entries exist
- entry, _ = htree.Get([]byte(PrefixAttrWord + "aaa" + "ddd"))
- pos = entry.(*indexEntry).WordPos["123"]
- if fmt.Sprint(bitutil.UnpackList(pos)) != "[2]" {
- t.Error("Unexpected result:", fmt.Sprint(bitutil.UnpackList(pos)))
- return
- }
- entry, _ = htree.Get([]byte(PrefixAttrWord + "aaa" + "voldaaa"))
- if entry != nil {
- t.Error("Unexpected result")
- return
- }
- entry, _ = htree.Get([]byte(PrefixAttrWord + "aaa" + "vnewaaa"))
- pos = entry.(*indexEntry).WordPos["123"]
- if fmt.Sprint(bitutil.UnpackList(pos)) != "[1]" {
- t.Error("Unexpected result:", fmt.Sprint(bitutil.UnpackList(pos)))
- return
- }
- entry, _ = htree.Get([]byte(PrefixAttrWord + "bbb" + "vbbb"))
- pos = entry.(*indexEntry).WordPos["123"]
- if fmt.Sprint(bitutil.UnpackList(pos)) != "[1]" {
- t.Error("Unexpected result:", fmt.Sprint(bitutil.UnpackList(pos)))
- return
- }
- // Delete from the index
- im.updateIndex("123", nil, obj1)
- if res := countChildren(htree); res != 0 {
- t.Error("Unexpected number of children:", res)
- return
- }
- }
- func countChildren(tree *hash.HTree) int {
- var count int
- it := hash.NewHTreeIterator(tree)
- for it.HasNext() {
- it.Next()
- count++
- }
- return count
- }
- func TestAddRemoveIndexHashEntry(t *testing.T) {
- sm := storage.NewMemoryStorageManager("testsm")
- htree, _ := hash.NewHTree(sm)
- im := NewIndexManager(htree)
- oldsetting := CaseSensitiveWordIndex
- CaseSensitiveWordIndex = false
- im.addIndexHashEntry("mykey2", "myattr", "testvalue")
- im.addIndexHashEntry("mykey3", "myattr", "testvalue")
- sm.AccessMap[2] = storage.AccessCacheAndFetchError
- if err := im.addIndexHashEntry("mykey2", "myattr", "testvalue"); err != storage.ErrSlotNotFound {
- t.Error(err)
- return
- }
- if err := im.removeIndexHashEntry("mykey2", "myattr", "testvalue"); err != storage.ErrSlotNotFound {
- t.Error(err)
- return
- }
- if _, err := im.LookupValue("myattr", "testvalue"); !strings.Contains(err.Error(), "Slot not found") {
- t.Error(err)
- return
- }
- delete(sm.AccessMap, 2)
- // Test lookup
- res, _ := im.LookupValue("myattr", "testvalue")
- if fmt.Sprint(res) != "[mykey2 mykey3]" {
- t.Error("Unexpected lookup value result:", res)
- return
- }
- res, _ = im.LookupValue("myattr", "testvalue2")
- if fmt.Sprint(res) != "[]" {
- t.Error("Unexpected lookup value result:", res)
- return
- }
- im.removeIndexHashEntry("mykey2", "myattr", "testvalue")
- im.removeIndexHashEntry("mykey3", "myattr", "testvalue")
- if count := countChildren(htree); count != 0 {
- t.Error("Unexpected child count:", count)
- return
- }
- // Check handling of non-existent entries
- if res := im.removeIndexHashEntry("mykey4", "myattr", "testvalue"); res != nil {
- t.Error("Unexpected result:", res)
- return
- }
- // Test case sensitive case
- CaseSensitiveWordIndex = true
- im.addIndexHashEntry("mykey2", "myattr", "testValue")
- im.addIndexHashEntry("mykey3", "myattr", "testValue")
- if count := countChildren(htree); count != 1 {
- t.Error("Unexpected child count:", count)
- return
- }
- res, _ = im.LookupValue("myattr", "testvalue")
- if fmt.Sprint(res) != "[]" {
- t.Error("Unexpected lookup value result:", res)
- return
- }
- res, _ = im.LookupValue("myattr", "testValue")
- if fmt.Sprint(res) != "[mykey2 mykey3]" {
- t.Error("Unexpected lookup value result:", res)
- return
- }
- im.removeIndexHashEntry("mykey2", "myattr", "testvalue")
- im.removeIndexHashEntry("mykey3", "myattr", "testvalue")
- if count := countChildren(htree); count != 1 {
- t.Error("Unexpected child count:", count)
- return
- }
- im.removeIndexHashEntry("mykey2", "myattr", "testValue")
- im.removeIndexHashEntry("mykey3", "myattr", "testValue")
- if count := countChildren(htree); count != 0 {
- t.Error("Unexpected child count:", count)
- return
- }
- CaseSensitiveWordIndex = oldsetting
- }
- func TestAddRemoveIndexEntry(t *testing.T) {
- sm := storage.NewMemoryStorageManager("testsm")
- htree, _ := hash.NewHTree(sm)
- im := NewIndexManager(htree)
- im.addIndexEntry("mykey", "myattr", "myword", []uint64{1, 5, 7})
- im.addIndexEntry("mykey2", "myattr", "myword", []uint64{10, 12, 80})
- entry, _ := htree.Get([]byte(PrefixAttrWord + "myattr" + "myword"))
- pos := entry.(*indexEntry).WordPos["mykey"]
- if fmt.Sprint(bitutil.UnpackList(pos)) != "[1 5 7]" {
- t.Error("Unexpected result:", fmt.Sprint(bitutil.UnpackList(pos)))
- return
- }
- pos = entry.(*indexEntry).WordPos["mykey2"]
- if fmt.Sprint(bitutil.UnpackList(pos)) != "[10 12 80]" {
- t.Error("Unexpected result:", fmt.Sprint(bitutil.UnpackList(pos)))
- return
- }
- testAddIndexPanic(t, im)
- sm.AccessMap[2] = storage.AccessCacheAndFetchError
- if res := im.addIndexEntry("mykey2", "myattr", "myword", []uint64{10, 12, 80}); res != storage.ErrSlotNotFound {
- t.Error("Unexpected result:", res)
- return
- }
- if res := im.removeIndexEntry("mykey2", "myattr", "myword", []uint64{10, 12, 80}); res != storage.ErrSlotNotFound {
- t.Error("Unexpected result:", res)
- return
- }
- delete(sm.AccessMap, 2)
- im.removeIndexEntry("mykey", "myattr", "myword", []uint64{1, 5, 7})
- entry, _ = htree.Get([]byte(PrefixAttrWord + "myattr" + "myword"))
- if res := len(entry.(*indexEntry).WordPos); res != 1 {
- t.Error("Unexpected length:", res)
- return
- }
- pos = entry.(*indexEntry).WordPos["mykey2"]
- if fmt.Sprint(bitutil.UnpackList(pos)) != "[10 12 80]" {
- t.Error("Unexpected result:", fmt.Sprint(bitutil.UnpackList(pos)))
- return
- }
- if im.removeIndexEntry("mykey3", "myattr", "myword", []uint64{10, 12, 80}) != nil {
- t.Error("Unexpected result")
- return
- }
- entry, _ = htree.Get([]byte(PrefixAttrWord + "myattr" + "myword"))
- if len(entry.(*indexEntry).WordPos) != 1 {
- t.Error("Unexpected length")
- return
- }
- if im.removeIndexEntry("mykey2", "myattr2", "myword", []uint64{10, 12, 80}) != nil {
- t.Error("Unexpected result")
- return
- }
- entry, _ = htree.Get([]byte(PrefixAttrWord + "myattr" + "myword"))
- if len(entry.(*indexEntry).WordPos) != 1 {
- t.Error("Unexpected length")
- return
- }
- if posres := fmt.Sprint(bitutil.UnpackList(entry.(*indexEntry).WordPos["mykey2"])); posres != "[10 12 80]" {
- t.Error("Unexpected pos list:", posres)
- return
- }
- // Test adding in non standard order
- if im.addIndexEntry("mykey2", "myattr", "myword", []uint64{45, 13}) != nil {
- t.Error("Unexpected result")
- return
- }
- if posres := fmt.Sprint(bitutil.UnpackList(entry.(*indexEntry).WordPos["mykey2"])); posres != "[10 12 13 45 80]" {
- t.Error("Unexpected pos list:", posres)
- return
- }
- // Test removal in non standard order
- if im.removeIndexEntry("mykey2", "myattr", "myword", []uint64{10, 80, 45, 13}) != nil {
- t.Error("Unexpected result")
- return
- }
- if posres := fmt.Sprint(bitutil.UnpackList(entry.(*indexEntry).WordPos["mykey2"])); posres != "[12]" {
- t.Error("Unexpected pos list:", posres)
- return
- }
- }
- func TestIndexManagerHashErrors(t *testing.T) {
- sm := storage.NewMemoryStorageManager("testsm")
- htree, _ := hash.NewHTree(sm)
- im := NewIndexManager(htree)
- obj1 := make(map[string]string)
- obj1["aaa"] = "DDD voldaaa ddd"
- obj2 := make(map[string]string)
- obj2["aaa"] = "DDDe voldaaa ddd"
- im.Index("testkey", obj1)
- sm.AccessMap[4] = storage.AccessCacheAndFetchError
- if err := im.Index("testkey", obj1); err == nil {
- t.Error("Error expected")
- return
- }
- if err := im.Reindex("testkey", obj1, obj1); err == nil {
- t.Error("Error expected")
- return
- }
- if err := im.Deindex("testkey", obj1); err == nil {
- t.Error("Error expected")
- return
- }
- sm.AccessMap[5] = storage.AccessUpdateError
- if err := im.Reindex("testkey", obj1, obj2); err == nil {
- t.Error("Error expected")
- return
- }
- delete(sm.AccessMap, 4)
- }
- func testAddIndexPanic(t *testing.T, in *IndexManager) {
- defer func() {
- if r := recover(); r == nil {
- t.Error("Adding empty pos list did not cause a panic.")
- }
- }()
- in.addIndexEntry("", "", "", []uint64{})
- }
- func TestExtractWords(t *testing.T) {
- oldsetting := CaseSensitiveWordIndex
- CaseSensitiveWordIndex = false
- ws := extractWords(" aaa BBB ;, ccc...aaa ddd-bbb xxxx aaaa test1\n" +
- "test2 xxxx bbb")
- if res := ws.String(); res != "WordSet:\n"+
- " aaa [1 4]\n"+
- " aaaa [8]\n"+
- " bbb [2 6 12]\n"+
- " ccc [3]\n"+
- " ddd [5]\n"+
- " test1 [9]\n"+
- " test2 [10]\n"+
- " xxxx [7 11]\n" {
- t.Error("Unexpected WordSet string result:", res)
- }
- CaseSensitiveWordIndex = true
- ws = extractWords(" aaa BBB ccc aaa ddd bbb xxxx aaaa test1\n" +
- "test2 xxxx bbb")
- if res := ws.String(); res != "WordSet:\n"+
- " BBB [2]\n"+
- " aaa [1 4]\n"+
- " aaaa [8]\n"+
- " bbb [6 12]\n"+
- " ccc [3]\n"+
- " ddd [5]\n"+
- " test1 [9]\n"+
- " test2 [10]\n"+
- " xxxx [7 11]\n" {
- t.Error("Unexpected WordSet string result:", res)
- }
- CaseSensitiveWordIndex = oldsetting
- }
- func TestWordSet(t *testing.T) {
- ws := newWordSet(1)
- ws2 := newWordSet(1)
- ws3 := newWordSet(1)
- ws.Add("aaa", 1)
- ws.Add("bbb", 2)
- ws2.Add("bbb", 3)
- ws3.Add("ccc", 4)
- if !ws.Has("bbb") || ws.Has("ccc") {
- t.Error("Unexpected has result")
- return
- }
- if res := ws.String(); res != "WordSet:\n"+
- " aaa [1]\n"+
- " bbb [2]\n" {
- t.Error("Unexpected string result:", res)
- return
- }
- ws.AddAll(ws2)
- if res := ws2.String(); res != "WordSet:\n"+
- " bbb [3]\n" {
- t.Error("Unexpected string result:", res)
- return
- }
- if res := ws.String(); res != "WordSet:\n"+
- " aaa [1]\n"+
- " bbb [2 3]\n" {
- t.Error("Unexpected string result:", res)
- return
- }
- ws.Add("bbb", 1)
- ws.AddAll(ws3)
- if res := ws.String(); res != "WordSet:\n"+
- " aaa [1]\n"+
- " bbb [1 2 3]\n"+
- " ccc [4]\n" {
- t.Error("Unexpected string result:", res)
- return
- }
- ws.Remove("aaa", 1)
- ws.Remove("bbb", 2)
- ws.Remove("bbb", 1)
- if res := ws.String(); res != "WordSet:\n"+
- " bbb [3]\n"+
- " ccc [4]\n" {
- t.Error("Unexpected string result:", res)
- return
- }
- ws.Add("bbb", 4)
- ws.Add("bbb", 8)
- ws.Add("bbb", 10)
- ws4 := newWordSet(1)
- ws4.Add("bbb", 2)
- ws4.Add("bbb", 3)
- ws4.Add("bbb", 4)
- ws4.Add("bbb", 8)
- if res := ws.String(); res != "WordSet:\n"+
- " bbb [3 4 8 10]\n"+
- " ccc [4]\n" {
- t.Error("Unexpected string result:", res)
- return
- }
- ws.RemoveAll(ws4)
- if res := ws.String(); res != "WordSet:\n"+
- " bbb [10]\n"+
- " ccc [4]\n" {
- t.Error("Unexpected string result:", res)
- return
- }
- if res := fmt.Sprint(ws4.Pos("bbb")); res != "[2 3 4 8]" {
- t.Error("Unexpected pos result:", res)
- return
- }
- if res := ws4.Pos("abb"); res != nil {
- t.Error("Unexpected pos result:", res)
- return
- }
- // Test double entries
- ws.Add("ccc", 3)
- ws.Add("ccc", 5)
- ws.Add("ccc", 4)
- if res := ws.String(); res != "WordSet:\n"+
- " bbb [10]\n"+
- " ccc [3 4 5]\n" {
- t.Error("Unexpected string result:", res)
- return
- }
- }
- func TestRemoveDuplicates(t *testing.T) {
- if res := fmt.Sprint(removeDuplicates([]uint64{1, 2, 2, 3})); res != "[1 2 3]" {
- t.Error("Unexpected remove duplicates result:", res)
- return
- }
- if res := fmt.Sprint(removeDuplicates([]uint64{})); res != "[]" {
- t.Error("Unexpected remove duplicates result:", res)
- return
- }
- }
- func TestIndexManagerString(t *testing.T) {
- sm := storage.NewMemoryStorageManager("testsm")
- htree, _ := hash.NewHTree(sm)
- im := NewIndexManager(htree)
- obj1 := make(map[string]string)
- obj1["aaa"] = "bbb"
- im.Index("testkey", obj1)
- if res := im.String(); res != "IndexManager: 1\n"+
- " 1\"aaabbb\" map[testkey:[1]]\n"+
- " 2\"aaa\\b\\xf8\\xe0&\\fdA\\x85\\x10\\xce\\xfb+\\x06\\xee\\xe5\\xcd\" map[testkey:[]]\n" {
- t.Error("Unexpected string output:", res)
- return
- }
- }
|