1package mnemonicode
2
3import (
4	"bytes"
5	"encoding/hex"
6	"fmt"
7	"strings"
8	"testing"
9
10	"golang.org/x/text/transform"
11)
12
13func TestWordsReq(t *testing.T) {
14	for i, n := range []int{0, 1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9, 9, 10} {
15		r := WordsRequired(i)
16		if r != n {
17			t.Errorf("WordsRequired(%d) returned %d, expected %d", i, r, n)
18		}
19	}
20}
21
22var testData = []struct {
23	hex   string
24	words []string
25}{
26	{"01", []string{"acrobat"}},
27	{"0102", []string{"opera", "academy"}},
28	{"010203", []string{"kayak", "cement", "ego"}},
29	{"01020304", []string{"papa", "twist", "alpine"}},
30	{"0102030405", []string{"papa", "twist", "alpine", "admiral"}},
31	{"010203040506", []string{"papa", "twist", "alpine", "shine", "academy"}},
32	{"01020304050607", []string{"papa", "twist", "alpine", "chess", "flute", "ego"}},
33	{"0102030405060708", []string{"papa", "twist", "alpine", "content", "sailor", "athena"}},
34	{"00", []string{"academy"}},
35	{"5A06", []string{"academy", "acrobat"}},
36	{"FE5D28", []string{"academy", "acrobat", "fax"}},
37	{"A2B55000", []string{"academy", "acrobat", "active"}},
38	{"A2B5500003", []string{"academy", "acrobat", "active", "actor"}},
39	{"A2B550006B19", []string{"academy", "acrobat", "active", "actor", "adam"}},
40	{"A2B550000F7128", []string{"academy", "acrobat", "active", "actor", "adam", "fax"}},
41	{"A2B550009FCFC900", []string{"academy", "acrobat", "active", "actor", "adam", "admiral"}},
42	{"FF", []string{"exact"}},
43	{"FFFF", []string{"nevada", "archive"}},
44	{"FFFFFF", []string{"claudia", "photo", "yes"}},
45	{"FFFFFFFF", []string{"natural", "analyze", "verbal"}},
46	{"123456789ABCDEF123456789ABCDEF012345", []string{
47		"plastic", "roger", "vincent", "pilgrim", "flame", "secure", "apropos", "polka", "earth", "radio", "modern", "aladdin", "marion", "airline"}},
48}
49
50func compareWordList(tb testing.TB, expected, got []string, args ...interface{}) {
51	fail := false
52	if len(expected) != len(got) {
53		fail = true
54	}
55	for i := 0; !fail && i < len(expected); i++ {
56		fail = expected[i] != got[i]
57	}
58	if fail {
59		prefix := ""
60		if len(args) > 0 {
61			prefix += fmt.Sprintln(args...)
62			prefix = prefix[:len(prefix)-1] + ": "
63		}
64		tb.Errorf("%vexpected %v, got %v", prefix, expected, got)
65	}
66}
67
68func TestEncodeWordList(t *testing.T) {
69	var result []string
70	for i, d := range testData {
71		raw, err := hex.DecodeString(d.hex)
72		if err != nil {
73			t.Fatal("bad test data:", i, err)
74		}
75		result = EncodeWordList(result, raw)
76		compareWordList(t, d.words, result, i, d.hex)
77		result = result[:0]
78	}
79}
80
81func TestDecodeWordList(t *testing.T) {
82	var result []byte
83	var err error
84	for i, d := range testData {
85		raw, _ := hex.DecodeString(d.hex)
86		result, err = DecodeWordList(result, d.words)
87		if err != nil {
88			t.Errorf("%2d %v failed: %v", i, d.words, err)
89			continue
90		}
91		if !bytes.Equal(raw, result) {
92			t.Errorf("%2d %v expected %v got %v", i, d.words, raw, result)
93		}
94		result = result[:0]
95	}
96}
97
98func TestEncodeTransformer(t *testing.T) {
99	cfg := NewDefaultConfig()
100	cfg.GroupSeparator = " "
101	enc := NewEncodeTransformer(cfg)
102	for i, d := range testData {
103		raw, err := hex.DecodeString(d.hex)
104		if err != nil {
105			t.Fatal("bad test data:", i, err)
106		}
107		result, _, err := transform.Bytes(enc, raw)
108		if err != nil {
109			t.Errorf("%2d %v failed: %v", i, d.words, err)
110			continue
111		}
112		//t.Logf("%q", result)
113		words := strings.Fields(string(result))
114		compareWordList(t, d.words, words, i, d.hex)
115	}
116
117}
118
119func TestDecodeTransformer(t *testing.T) {
120	dec := NewDecodeTransformer()
121	for i, d := range testData {
122		raw, _ := hex.DecodeString(d.hex)
123		words := strings.Join(d.words, " ")
124		result, _, err := transform.Bytes(dec, []byte(words))
125		if err != nil {
126			t.Errorf("%2d %v failed: %v", i, d.words, err)
127			continue
128		}
129		if !bytes.Equal(raw, result) {
130			t.Errorf("%2d %v expected %v got %v", i, d.words, raw, result)
131		}
132	}
133}
134
135func TestEncodeFormatting(t *testing.T) {
136	raw, _ := hex.DecodeString(testData[20].hex)
137	input := string(raw)
138	//words := testData[20].words
139	tests := []struct {
140		cfg       *Config
141		formatted string
142	}{
143		{nil, "plastic roger   vincent - pilgrim flame   secure  - apropos polka   earth  \nradio   modern  aladdin - marion  airline"},
144		{&Config{
145			LinePrefix:     "{P}",
146			LineSuffix:     "{S}\n",
147			WordSeparator:  "{w}",
148			GroupSeparator: "{g}",
149			WordsPerGroup:  2,
150			GroupsPerLine:  2,
151			WordPadding:    '·',
152		},
153			`{P}plastic{w}roger··{g}vincent{w}pilgrim{S}
154{P}flame··{w}secure·{g}apropos{w}polka··{S}
155{P}earth··{w}radio··{g}modern·{w}aladdin{S}
156{P}marion·{w}airline`},
157	}
158	for i, d := range tests {
159		enc := NewEncodeTransformer(d.cfg)
160		result, _, err := transform.String(enc, input)
161		if err != nil {
162			t.Errorf("%2d transform failed: %v", i, err)
163			continue
164		}
165		if result != d.formatted {
166			t.Errorf("%2d expected:\n%q\ngot:\n%q", i, d.formatted, result)
167		}
168	}
169}
170
171func BenchmarkEncodeWordList(b *testing.B) {
172	// the list of all known words (except the short end words)
173	data, err := DecodeWordList(nil, wordList[:base])
174	if err != nil {
175		b.Fatal("DecodeWordList failed:", err)
176	}
177	b.SetBytes(int64(len(data)))
178	b.ReportAllocs()
179	b.ResetTimer()
180	var words []string
181	for i := 0; i < b.N; i++ {
182		words = EncodeWordList(words[:0], data)
183	}
184}
185
186func BenchmarkDencodeWordList(b *testing.B) {
187	b.ReportAllocs()
188	var buf []byte
189	var err error
190	// decode the list of all known words (except the short end words)
191	for i := 0; i < b.N; i++ {
192		buf, err = DecodeWordList(buf[:0], wordList[:base])
193		if err != nil {
194			b.Fatal("DecodeWordList failed:", err)
195		}
196	}
197	b.SetBytes(int64(len(buf)))
198}
199
200func BenchmarkEncodeTransformer(b *testing.B) {
201	// the list of all known words (except the short end words)
202	data, err := DecodeWordList(nil, wordList[:base])
203	if err != nil {
204		b.Fatal("DecodeWordList failed:", err)
205	}
206	enc := NewEncodeTransformer(nil)
207	b.SetBytes(int64(len(data)))
208	b.ReportAllocs()
209	b.ResetTimer()
210	for i := 0; i < b.N; i++ {
211		_, _, err := transform.Bytes(enc, data)
212		if err != nil {
213			b.Fatal("encode transformer error:", err)
214		}
215	}
216}
217
218func BenchmarkDecodeTransformer(b *testing.B) {
219	data, err := DecodeWordList(nil, wordList[:base])
220	if err != nil {
221		b.Fatal("DecodeWordList failed:", err)
222	}
223	enc := NewEncodeTransformer(nil)
224	words, _, err := transform.Bytes(enc, data)
225	if err != nil {
226		b.Fatal("encode transformer error:", err)
227	}
228	b.SetBytes(int64(len(data)))
229	dec := NewDecodeTransformer()
230	b.ReportAllocs()
231	b.ResetTimer()
232	for i := 0; i < b.N; i++ {
233		_, _, err := transform.Bytes(dec, words)
234		if err != nil {
235			b.Fatal("decode transformer error:", err)
236		}
237	}
238}
239