1package msgp
2
3import (
4	"bytes"
5	"reflect"
6	"testing"
7)
8
9func TestRemove(t *testing.T) {
10	var buf bytes.Buffer
11	w := NewWriter(&buf)
12	w.WriteMapHeader(3)
13	w.WriteString("first")
14	w.WriteFloat64(-3.1)
15	w.WriteString("second")
16	w.WriteString("DELETE ME!!!")
17	w.WriteString("third")
18	w.WriteBytes([]byte("blah"))
19	w.Flush()
20
21	raw := Remove("second", buf.Bytes())
22
23	m, _, err := ReadMapStrIntfBytes(raw, nil)
24	if err != nil {
25		t.Fatal(err)
26	}
27	if len(m) != 2 {
28		t.Errorf("expected %d fields; found %d", 2, len(m))
29	}
30	if _, ok := m["first"]; !ok {
31		t.Errorf("field %q not found", "first")
32	}
33	if _, ok := m["third"]; !ok {
34		t.Errorf("field %q not found", "third")
35	}
36	if _, ok := m["second"]; ok {
37		t.Errorf("field %q (deleted field) still present", "second")
38	}
39}
40
41func TestLocate(t *testing.T) {
42	var buf bytes.Buffer
43	en := NewWriter(&buf)
44	en.WriteMapHeader(2)
45	en.WriteString("thing_one")
46	en.WriteString("value_one")
47	en.WriteString("thing_two")
48	en.WriteFloat64(2.0)
49	en.Flush()
50
51	field := Locate("thing_one", buf.Bytes())
52	if len(field) == 0 {
53		t.Fatal("field not found")
54	}
55
56	if !HasKey("thing_one", buf.Bytes()) {
57		t.Fatal("field not found")
58	}
59
60	var zbuf bytes.Buffer
61	w := NewWriter(&zbuf)
62	w.WriteString("value_one")
63	w.Flush()
64
65	if !bytes.Equal(zbuf.Bytes(), field) {
66		t.Errorf("got %q; wanted %q", field, zbuf.Bytes())
67	}
68
69	zbuf.Reset()
70	w.WriteFloat64(2.0)
71	w.Flush()
72	field = Locate("thing_two", buf.Bytes())
73	if len(field) == 0 {
74		t.Fatal("field not found")
75	}
76	if !bytes.Equal(zbuf.Bytes(), field) {
77		t.Errorf("got %q; wanted %q", field, zbuf.Bytes())
78	}
79
80	field = Locate("nope", buf.Bytes())
81	if len(field) != 0 {
82		t.Fatalf("wanted a zero-length returned slice")
83	}
84
85}
86
87func TestReplace(t *testing.T) {
88	// there are 4 cases that need coverage:
89	//  - new value is smaller than old value
90	//  - new value is the same size as the old value
91	//  - new value is larger than old, but fits within cap(b)
92	//  - new value is larger than old, and doesn't fit within cap(b)
93
94	var buf bytes.Buffer
95	en := NewWriter(&buf)
96	en.WriteMapHeader(3)
97	en.WriteString("thing_one")
98	en.WriteString("value_one")
99	en.WriteString("thing_two")
100	en.WriteFloat64(2.0)
101	en.WriteString("some_bytes")
102	en.WriteBytes([]byte("here are some bytes"))
103	en.Flush()
104
105	// same-size replacement
106	var fbuf bytes.Buffer
107	w := NewWriter(&fbuf)
108	w.WriteFloat64(4.0)
109	w.Flush()
110
111	// replace 2.0 with 4.0 in field two
112	raw := Replace("thing_two", buf.Bytes(), fbuf.Bytes())
113	if len(raw) == 0 {
114		t.Fatal("field not found")
115	}
116	var err error
117	m := make(map[string]interface{})
118	m, _, err = ReadMapStrIntfBytes(raw, m)
119	if err != nil {
120		t.Logf("%q", raw)
121		t.Fatal(err)
122	}
123
124	if !reflect.DeepEqual(m["thing_two"], 4.0) {
125		t.Errorf("wanted %v; got %v", 4.0, m["thing_two"])
126	}
127
128	// smaller-size replacement
129	// replace 2.0 with []byte("hi!")
130	fbuf.Reset()
131	w.WriteBytes([]byte("hi!"))
132	w.Flush()
133	raw = Replace("thing_two", raw, fbuf.Bytes())
134	if len(raw) == 0 {
135		t.Fatal("field not found")
136	}
137
138	m, _, err = ReadMapStrIntfBytes(raw, m)
139	if err != nil {
140		t.Logf("%q", raw)
141		t.Fatal(err)
142	}
143
144	if !reflect.DeepEqual(m["thing_two"], []byte("hi!")) {
145		t.Errorf("wanted %v; got %v", []byte("hi!"), m["thing_two"])
146	}
147
148	// larger-size replacement
149	fbuf.Reset()
150	w.WriteBytes([]byte("some even larger bytes than before"))
151	w.Flush()
152	raw = Replace("some_bytes", raw, fbuf.Bytes())
153	if len(raw) == 0 {
154		t.Logf("%q", raw)
155		t.Fatal(err)
156	}
157
158	m, _, err = ReadMapStrIntfBytes(raw, m)
159	if err != nil {
160		t.Logf("%q", raw)
161		t.Fatal(err)
162	}
163
164	if !reflect.DeepEqual(m["some_bytes"], []byte("some even larger bytes than before")) {
165		t.Errorf("wanted %v; got %v", []byte("hello there!"), m["some_bytes"])
166	}
167
168	// identical in-place replacement
169	field := Locate("some_bytes", raw)
170	newraw := CopyReplace("some_bytes", raw, field)
171
172	if !bytes.Equal(newraw, raw) {
173		t.Logf("in: %q", raw)
174		t.Logf("out: %q", newraw)
175		t.Error("bytes not equal after copyreplace")
176	}
177}
178
179func BenchmarkLocate(b *testing.B) {
180	var buf bytes.Buffer
181	en := NewWriter(&buf)
182	en.WriteMapHeader(3)
183	en.WriteString("thing_one")
184	en.WriteString("value_one")
185	en.WriteString("thing_two")
186	en.WriteFloat64(2.0)
187	en.WriteString("thing_three")
188	en.WriteBytes([]byte("hello!"))
189	en.Flush()
190
191	raw := buf.Bytes()
192	// bytes/s will be the number of bytes traversed per unit of time
193	field := Locate("thing_three", raw)
194	b.SetBytes(int64(len(raw) - len(field)))
195	b.ReportAllocs()
196	b.ResetTimer()
197	for i := 0; i < b.N; i++ {
198		Locate("thing_three", raw)
199	}
200}
201