1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.emailcommon.mail;
18
19import android.test.AndroidTestCase;
20import android.test.suitebuilder.annotation.SmallTest;
21import org.apache.james.mime4j.decoder.DecoderUtil;
22
23import java.io.UnsupportedEncodingException;
24import java.net.URLEncoder;
25
26/**
27 * This is a series of unit tests for the Address class.  These tests must be locally
28 * complete - no server(s) required.
29 */
30@SmallTest
31public class AddressUnitTests extends AndroidTestCase {
32
33    private static final String MULTI_ADDRESSES_LIST =
34            "noname1@dom1.com, "
35            + "<noname2@dom2.com>, "
36            + "simple name <address3@dom3.org>, "
37            + "\"name,4\" <address4@dom4.org>,"
38            + "\"big \\\"G\\\"\" <bigG@dom5.net>,"
39            + "\u65E5\u672C\u8A9E <address6@co.jp>,"
40            + "\"\u65E5\u672C\u8A9E\" <address7@co.jp>,"
41            + "\uD834\uDF01\uD834\uDF46 <address8@ne.jp>,"
42            + "\"\uD834\uDF01\uD834\uDF46\" <address9@ne.jp>,"
43            + "noname@dom.com <noname@dom.com>" // personal == address
44            ;
45    private static final int MULTI_ADDRESSES_COUNT = 10;
46
47    private static final Address PACK_ADDR_1 = new Address("john@gmail.com", "John Doe");
48    private static final Address PACK_ADDR_2 = new Address("foo@bar.com", null);
49    private static final Address PACK_ADDR_3 = new Address(
50            "mar.y+test@gmail.com", "Mar-y, B; B*arr");
51    private static final Address[][] PACK_CASES = {
52        {PACK_ADDR_2}, {PACK_ADDR_1},
53        {PACK_ADDR_1, PACK_ADDR_2}, {PACK_ADDR_2, PACK_ADDR_1},
54        {PACK_ADDR_1, PACK_ADDR_3}, {PACK_ADDR_2, PACK_ADDR_2},
55        {PACK_ADDR_1, PACK_ADDR_2, PACK_ADDR_3}, {PACK_ADDR_3, PACK_ADDR_1, PACK_ADDR_2}
56    };
57
58    Address mAddress1;
59    Address mAddress2;
60    Address mAddress3;
61
62    /**
63     * Setup code.  We generate a handful of Address objects
64     */
65    @Override
66    protected void setUp() throws Exception {
67        super.setUp();
68
69        mAddress1 = new Address("address1", "personal1");
70        mAddress2 = new Address("address2", "");
71        mAddress3 = new Address("address3", null);
72    }
73
74    // see documentation of DecoderUtil.decodeEncodedWords() for details
75    private String padEncoded(String s) {
76        return "=?UTF-8?B?" + s + "?=";
77    }
78
79    /**
80     * Generate strings of incresing lenght by taking prefix substrings.
81     * For each of them, compare with the decoding of the precomputed base-64 encoding.
82     */
83    public void testBase64Decode() {
84        String testString = "xyza\0\"";
85        String base64Encoded[] = {"", "eA==", "eHk=", "eHl6", "eHl6YQ==", "eHl6YQA=", "eHl6YQAi"};
86        int len = testString.length();
87        for (int i = 1; i <= len; ++i) {
88            String encoded = padEncoded(base64Encoded[i]);
89            String decoded = DecoderUtil.decodeEncodedWords(encoded);
90            String prefix = testString.substring(0, i);
91            assertEquals(""+i, prefix, decoded);
92        }
93    }
94
95    /**
96     * Test for setAddress().
97     */
98    public void testSetAddress() {
99        String bareAddress = "user1@dom1.com";
100        String bracketAddress = "<user2@dom2.com>";
101
102        Address address = new Address(bareAddress);
103        assertEquals("bare address", "user1@dom1.com", address.getAddress());
104
105        address.setAddress(bracketAddress);
106        assertEquals("bracket address", "user2@dom2.com", address.getAddress());
107    }
108
109    /**
110     * Test for empty setPersonal().
111     */
112    public void testNullPersonal() {
113        Address address = new Address("user1@dom1.org");
114        assertNull("no name", address.getPersonal());
115
116        address.setPersonal(null);
117        assertNull("null name", address.getPersonal());
118
119        address.setPersonal("");
120        assertNull("empty name", address.getPersonal());
121
122        address.setPersonal("\"\"");
123        assertNull("quoted empty address", address.getPersonal());
124    }
125
126    /**
127     * Test for setPersonal().
128     */
129    public void testSetPersonal() {
130        Address address = new Address("user1@dom1.net", "simple name");
131        assertEquals("simple name", "simple name", address.getPersonal());
132
133        address.setPersonal("big \\\"G\\\"");
134        assertEquals("quoted name", "big \"G\"", address.getPersonal());
135
136        address.setPersonal("=?UTF-8?Q?big \"G\"?=");
137        assertEquals("quoted printable name", "big \"G\"", address.getPersonal());
138
139        address.setPersonal("=?UTF-8?B?YmlnICJHIg==?=");
140        assertEquals("base64 encoded name", "big \"G\"", address.getPersonal());
141    }
142
143    /**
144     * Test for setPersonal() with utf-16 and utf-32.
145     */
146    public void testSetPersonalMultipleEncodings() {
147        Address address = new Address("user1@dom1.co.jp", "=?UTF-8?B?5bK45pys?=");
148        assertEquals("base64 utf-16 name", "\u5CB8\u672C", address.getPersonal());
149
150        address.setPersonal("\"=?UTF-8?Q?=E5=B2=B8=E6=9C=AC?=\"");
151        assertEquals("quoted printable utf-16 name", "\u5CB8\u672C", address.getPersonal());
152
153        address.setPersonal("=?ISO-2022-JP?B?GyRCNF9LXBsoQg==?=");
154        assertEquals("base64 jis encoded name", "\u5CB8\u672C", address.getPersonal());
155
156        address.setPersonal("\"=?UTF-8?B?8J2MgfCdjYY=?=\"");
157        assertEquals("base64 utf-32 name", "\uD834\uDF01\uD834\uDF46", address.getPersonal());
158
159        address.setPersonal("=?UTF-8?Q?=F0=9D=8C=81=F0=9D=8D=86?=");
160        assertEquals("quoted printable utf-32 name",
161                "\uD834\uDF01\uD834\uDF46", address.getPersonal());
162    }
163
164    /**
165     * TODO: more in-depth tests for parse()
166     */
167
168    /**
169     * Simple quick checks of empty-input edge conditions for parse()
170     *
171     * NOTE:  This is not a claim that these edge cases are "correct", only to maintain consistent
172     * behavior while I am changing some of the code in the function under test.
173     */
174    public void testEmptyParse() {
175        Address[] result;
176
177        // null input => empty array
178        result = Address.parse(null);
179        assertTrue("parsing null address", result != null && result.length == 0);
180
181        // empty string input => empty array
182        result = Address.parse("");
183        assertTrue("parsing zero-length", result != null && result.length == 0);
184
185        // spaces
186        result = Address.parse("   ");
187        assertTrue("parsing spaces", result != null && result.length == 0);
188
189        // spaces with comma
190        result = Address.parse("  ,  ");
191        assertTrue("parsing spaces with comma", result != null && result.length == 0);
192    }
193
194    /**
195     * Test parsing for single address.
196     */
197    public void testSingleParse() {
198        Address[] address1 = Address.parse("address1@dom1.com");
199        assertEquals("bare address count", 1, address1.length);
200        assertEquals("bare address", "address1@dom1.com", address1[0].getAddress());
201        assertNull("name of bare address", address1[0].getPersonal());
202
203        Address[] address2 = Address.parse("<address2@dom2.com>");
204        assertEquals("bracket address count", 1, address2.length);
205        assertEquals("bracket address", "address2@dom2.com", address2[0].getAddress());
206        assertNull("name of bracket address", address2[0].getPersonal());
207
208        Address[] address3 = Address.parse("first last <address3@dom3.org>");
209        assertEquals("address with name count", 1, address3.length);
210        assertEquals("address with name", "address3@dom3.org", address3[0].getAddress());
211        assertEquals("name of address with name", "first last", address3[0].getPersonal());
212
213        Address[] address4 = Address.parse("\"first,last\" <address4@dom4.org>");
214        assertEquals("address with quoted name count", 1, address4.length);
215        assertEquals("address with quoted name", "address4@dom4.org", address4[0].getAddress());
216        assertEquals("name of address with quoted name", "first,last", address4[0].getPersonal());
217    }
218
219    /**
220     * Test parsing for illegal address.
221     */
222    public void testIllegalParse() {
223        Address[] address1 = Address.parse("address1");
224        assertEquals("no atmark", 0, address1.length);
225
226        Address[] address2 = Address.parse("address2@");
227        assertEquals("no domain", 0, address2.length);
228
229        Address[] address3 = Address.parse("@dom3.com");
230        assertEquals("no local part", 0, address3.length);
231
232        Address[] address4 = Address.parse("address4@sub@dom4.org");
233        assertEquals("more than one atmark", 0, address4.length);
234
235        Address[] address5 = Address.parse("address5@dom5");
236        assertEquals("not dot in domain part", 0, address5.length);
237
238        Address[] address6 = Address.parse("address6@dom6.com.");
239        assertEquals("domain ends with dot", 0, address6.length);
240
241        Address[] address7 = Address.parse("address7@.dom7.org");
242        assertEquals("domain starts with dot", 0, address7.length);
243    }
244
245    /**
246     * Test parsing for address part.
247     */
248    public void testParsingAddress() {
249        Address[] addresses = Address.parse("address1@dom1.net, <address2@dom2.com>");
250        assertEquals("address count", 2, addresses.length);
251
252        assertEquals("bare address", "address1@dom1.net", addresses[0].getAddress());
253        assertNull("bare address name", addresses[0].getPersonal());
254
255        assertEquals("bracket address", "address2@dom2.com", addresses[1].getAddress());
256        assertNull("bracket address name", addresses[1].getPersonal());
257    }
258
259    /**
260     * Test parsing for simple name part.
261     */
262    public void testParsingSimpleName() {
263        Address[] addresses = Address.parse(
264                "name 1 <address1@dom1.net>, " +
265                "\"name,2\" <address2@dom2.org>");
266        assertEquals("address count", 2, addresses.length);
267
268        assertEquals("bare name address", "address1@dom1.net", addresses[0].getAddress());
269        assertEquals("bare name", "name 1", addresses[0].getPersonal());
270
271        assertEquals("double quoted name address", "address2@dom2.org", addresses[1].getAddress());
272        assertEquals("double quoted name", "name,2", addresses[1].getPersonal());
273    }
274
275    /**
276     * Test parsing for utf-16 name part.
277     */
278    public void testParsingUtf16Name() {
279        Address[] addresses = Address.parse(
280                "\u3042\u3044\u3046 \u3048\u304A <address1@dom1.jp>, " +
281                "\"\u3042\u3044\u3046,\u3048\u304A\" <address2@dom2.jp>");
282        assertEquals("address count", 2, addresses.length);
283
284        assertEquals("bare utf-16 name address", "address1@dom1.jp", addresses[0].getAddress());
285        assertEquals("bare utf-16 name",
286                "\u3042\u3044\u3046 \u3048\u304A", addresses[0].getPersonal());
287
288        assertEquals("double quoted utf-16 name address",
289                "address2@dom2.jp", addresses[1].getAddress());
290        assertEquals("double quoted utf-16 name",
291                "\u3042\u3044\u3046,\u3048\u304A", addresses[1].getPersonal());
292    }
293
294    /**
295     * Test parsing for utf-32 name part.
296     */
297    public void testParsingUtf32Name() {
298        Address[] addresses = Address.parse(
299                "\uD834\uDF01\uD834\uDF46 \uD834\uDF22 <address1@dom1.net>, " +
300                "\"\uD834\uDF01\uD834\uDF46,\uD834\uDF22\" <address2@dom2.com>");
301        assertEquals("address count", 2, addresses.length);
302
303        assertEquals("bare utf-32 name address", "address1@dom1.net", addresses[0].getAddress());
304        assertEquals("bare utf-32 name",
305                "\uD834\uDF01\uD834\uDF46 \uD834\uDF22", addresses[0].getPersonal());
306
307        assertEquals("double quoted utf-32 name address",
308                "address2@dom2.com", addresses[1].getAddress());
309        assertEquals("double quoted utf-32 name",
310                "\uD834\uDF01\uD834\uDF46,\uD834\uDF22", addresses[1].getPersonal());
311    }
312
313    /**
314     * Test parsing for multi addresses.
315     */
316    public void testParseMulti() {
317        Address[] addresses = Address.parse(MULTI_ADDRESSES_LIST);
318
319        assertEquals("multi addrsses count", MULTI_ADDRESSES_COUNT, addresses.length);
320
321        assertEquals("no name 1 address", "noname1@dom1.com", addresses[0].getAddress());
322        assertNull("no name 1 name", addresses[0].getPersonal());
323        assertEquals("no name 2 address", "noname2@dom2.com", addresses[1].getAddress());
324        assertNull("no name 2 name", addresses[1].getPersonal());
325        assertEquals("simple name address", "address3@dom3.org", addresses[2].getAddress());
326        assertEquals("simple name name", "simple name", addresses[2].getPersonal());
327        assertEquals("double quoted name address", "address4@dom4.org", addresses[3].getAddress());
328        assertEquals("double quoted name name", "name,4", addresses[3].getPersonal());
329        assertEquals("quoted name address", "bigG@dom5.net", addresses[4].getAddress());
330        assertEquals("quoted name name", "big \"G\"", addresses[4].getPersonal());
331        assertEquals("utf-16 name address", "address6@co.jp", addresses[5].getAddress());
332        assertEquals("utf-16 name name", "\u65E5\u672C\u8A9E", addresses[5].getPersonal());
333        assertEquals("utf-16 quoted name address", "address7@co.jp", addresses[6].getAddress());
334        assertEquals("utf-16 quoted name name", "\u65E5\u672C\u8A9E",
335                addresses[6].getPersonal());
336        assertEquals("utf-32 name address", "address8@ne.jp", addresses[7].getAddress());
337        assertEquals("utf-32 name name", "\uD834\uDF01\uD834\uDF46", addresses[7].getPersonal());
338        assertEquals("utf-32 quoted name address", "address9@ne.jp", addresses[8].getAddress());
339        assertEquals("utf-32 quoted name name", "\uD834\uDF01\uD834\uDF46",
340                addresses[8].getPersonal());
341    }
342
343    /**
344     * Test various combinations of the toString (single) method
345     */
346    public void testToStringSingle() {
347        Address[] addresses = Address.parse(MULTI_ADDRESSES_LIST);
348
349        assertEquals("multi addrsses count", MULTI_ADDRESSES_COUNT, addresses.length);
350
351        // test for toString() results.
352        assertEquals("no name 1", "noname1@dom1.com", addresses[0].toString());
353        assertEquals("no name 2", "noname2@dom2.com", addresses[1].toString());
354        assertEquals("simple name", "simple name <address3@dom3.org>", addresses[2].toString());
355        assertEquals("double quoted name",
356                "\"name,4\" <address4@dom4.org>", addresses[3].toString());
357        assertEquals("quoted name", "\"big \"G\"\" <bigG@dom5.net>", addresses[4].toString());
358        assertEquals("utf-16 name", "\u65E5\u672C\u8A9E <address6@co.jp>",
359                addresses[5].toString());
360        assertEquals("utf-16 quoted name", "\u65E5\u672C\u8A9E <address7@co.jp>",
361                addresses[6].toString());
362        assertEquals("utf-32 name", "\uD834\uDF01\uD834\uDF46 <address8@ne.jp>",
363                addresses[7].toString());
364        assertEquals("utf-32 quoted name", "\uD834\uDF01\uD834\uDF46 <address9@ne.jp>",
365                addresses[8].toString());
366        assertEquals("name==address", "noname@dom.com", addresses[9].toString());
367    }
368
369    /**
370     * Test various combinations of the toString (multi) method
371     */
372    public void testToStringMulti() {
373        final Address[] address = Address.parse("noname1@dom1.com");
374        final Address[] addresses = Address.parse(MULTI_ADDRESSES_LIST);
375
376        assertEquals("multi addrsses count", MULTI_ADDRESSES_COUNT, addresses.length);
377
378        {
379            String line = Address.toString(address);
380            assertEquals("toString multi-1",
381                    "noname1@dom1.com",
382                    line);
383        }
384        {
385            String line = Address.toString(addresses);
386            assertEquals("toString multi-n",
387                    "noname1@dom1.com,"
388                    + "noname2@dom2.com,"
389                    + "simple name <address3@dom3.org>,"
390                    + "\"name,4\" <address4@dom4.org>,"
391                    + "\"big \"G\"\" <bigG@dom5.net>,"
392                    + "\u65E5\u672C\u8A9E <address6@co.jp>,"
393                    + "\u65E5\u672C\u8A9E <address7@co.jp>,"
394                    + "\uD834\uDF01\uD834\uDF46 <address8@ne.jp>,"
395                    + "\uD834\uDF01\uD834\uDF46 <address9@ne.jp>,"
396                    + "noname@dom.com",
397                    line);
398        }
399
400        // With custom separator
401        {
402            String line = Address.toString(address, "$");
403            assertEquals("toString multi-1",
404                    "noname1@dom1.com",
405                    line);
406        }
407
408        {
409            String line = Address.toString(addresses, "$");
410            assertEquals("toString multi-n",
411                    "noname1@dom1.com$"
412                    + "noname2@dom2.com$"
413                    + "simple name <address3@dom3.org>$"
414                    + "\"name,4\" <address4@dom4.org>$"
415                    + "\"big \"G\"\" <bigG@dom5.net>$"
416                    + "\u65E5\u672C\u8A9E <address6@co.jp>$"
417                    + "\u65E5\u672C\u8A9E <address7@co.jp>$"
418                    + "\uD834\uDF01\uD834\uDF46 <address8@ne.jp>$"
419                    + "\uD834\uDF01\uD834\uDF46 <address9@ne.jp>$"
420                    + "noname@dom.com",
421                    line);
422        }
423    }
424
425    /**
426     * Test parsing for quoted and encoded name part.
427     */
428    public void testParsingQuotedEncodedName() {
429        Address[] addresses = Address.parse(
430                "\"big \\\"G\\\"\" <bigG@dom1.com>, =?UTF-8?B?5pel5pys6Kqe?= <address2@co.jp>");
431
432        assertEquals("address count", 2, addresses.length);
433
434        assertEquals("quoted name address", "bigG@dom1.com", addresses[0].getAddress());
435        assertEquals("quoted name", "big \"G\"", addresses[0].getPersonal());
436
437        assertEquals("encoded name address", "address2@co.jp", addresses[1].getAddress());
438        assertEquals("encoded name", "\u65E5\u672C\u8A9E", addresses[1].getPersonal());
439    }
440
441    /**
442     * Test various combinations of the toHeader (single) method
443     */
444    public void testToHeaderSingle() {
445        Address noName1 = new Address("noname1@dom1.com");
446        Address noName2 = new Address("<noname2@dom2.com>", "");
447        Address simpleName = new Address("address3@dom3.org", "simple name");
448        Address dquoteName = new Address("address4@dom4.org", "name,4");
449        Address quotedName = new Address("bigG@dom5.net", "big \"G\"");
450        Address utf16Name = new Address("<address6@co.jp>", "\"\u65E5\u672C\u8A9E\"");
451        Address utf32Name = new Address("<address8@ne.jp>", "\uD834\uDF01\uD834\uDF46");
452        Address sameName = new Address("address@dom.org", "address@dom.org");
453
454        // test for internal states.
455        assertEquals("no name 1 address", "noname1@dom1.com", noName1.getAddress());
456        assertNull("no name 1 name", noName1.getPersonal());
457        assertEquals("no name 2 address", "noname2@dom2.com", noName2.getAddress());
458        assertNull("no name 2 name", noName2.getPersonal());
459        assertEquals("simple name address", "address3@dom3.org", simpleName.getAddress());
460        assertEquals("simple name name", "simple name", simpleName.getPersonal());
461        assertEquals("double quoted name address", "address4@dom4.org", dquoteName.getAddress());
462        assertEquals("double quoted name name", "name,4", dquoteName.getPersonal());
463        assertEquals("quoted name address", "bigG@dom5.net", quotedName.getAddress());
464        assertEquals("quoted name name", "big \"G\"", quotedName.getPersonal());
465        assertEquals("utf-16 name address", "address6@co.jp", utf16Name.getAddress());
466        assertEquals("utf-16 name name", "\u65E5\u672C\u8A9E", utf16Name.getPersonal());
467        assertEquals("utf-32 name address", "address8@ne.jp", utf32Name.getAddress());
468        assertEquals("utf-32 name name", "\uD834\uDF01\uD834\uDF46", utf32Name.getPersonal());
469        assertEquals("name == address address", "address@dom.org", sameName.getAddress());
470        assertEquals("name == address name", "address@dom.org", sameName.getPersonal());
471
472        // Test for toHeader() results.
473        assertEquals("no name 1", "noname1@dom1.com", noName1.toHeader());
474        assertEquals("no name 2", "noname2@dom2.com", noName2.toHeader());
475        assertEquals("simple name", "simple name <address3@dom3.org>", simpleName.toHeader());
476        assertEquals("double quoted name", "\"name,4\" <address4@dom4.org>", dquoteName.toHeader());
477        assertEquals("quoted name", "\"big \\\"G\\\"\" <bigG@dom5.net>", quotedName.toHeader());
478        assertEquals("utf-16 name", "=?UTF-8?B?5pel5pys6Kqe?= <address6@co.jp>",
479                utf16Name.toHeader());
480        assertEquals("utf-32 name", "=?UTF-8?B?8J2MgfCdjYY=?= <address8@ne.jp>",
481                utf32Name.toHeader());
482        assertEquals("name == address", "\"address@dom.org\" <address@dom.org>",
483                sameName.toHeader());
484    }
485
486    /**
487     * Test various combinations of the toHeader (multi) method
488     */
489    public void testToHeaderMulti() {
490        Address noName1 = new Address("noname1@dom1.com");
491        Address noName2 = new Address("<noname2@dom2.com>", "");
492        Address simpleName = new Address("address3@dom3.org", "simple name");
493        Address dquoteName = new Address("address4@dom4.org", "name,4");
494        Address quotedName = new Address("bigG@dom5.net", "big \"G\"");
495        Address utf16Name = new Address("<address6@co.jp>", "\"\u65E5\u672C\u8A9E\"");
496        Address utf32Name = new Address("<address8@ne.jp>", "\uD834\uDF01\uD834\uDF46");
497
498        // test for internal states.
499        assertEquals("no name 1 address", "noname1@dom1.com", noName1.getAddress());
500        assertNull("no name 1 name", noName1.getPersonal());
501        assertEquals("no name 2 address", "noname2@dom2.com", noName2.getAddress());
502        assertNull("no name 2 name", noName2.getPersonal());
503        assertEquals("simple name address", "address3@dom3.org", simpleName.getAddress());
504        assertEquals("simple name name", "simple name", simpleName.getPersonal());
505        assertEquals("double quoted name address", "address4@dom4.org", dquoteName.getAddress());
506        assertEquals("double quoted name name", "name,4", dquoteName.getPersonal());
507        assertEquals("quoted name address", "bigG@dom5.net", quotedName.getAddress());
508        assertEquals("quoted name name", "big \"G\"", quotedName.getPersonal());
509        assertEquals("utf-16 name address", "address6@co.jp", utf16Name.getAddress());
510        assertEquals("utf-16 name name", "\u65E5\u672C\u8A9E", utf16Name.getPersonal());
511        assertEquals("utf-32 name address", "address8@ne.jp", utf32Name.getAddress());
512        assertEquals("utf-32 name name", "\uD834\uDF01\uD834\uDF46", utf32Name.getPersonal());
513
514        Address[] addresses = new Address[] {
515                noName1, noName2, simpleName, dquoteName, quotedName, utf16Name, utf32Name,
516        };
517        String line = Address.toHeader(addresses);
518
519        assertEquals("toHeader() multi",
520                "noname1@dom1.com, "
521                + "noname2@dom2.com, "
522                + "simple name <address3@dom3.org>, "
523                + "\"name,4\" <address4@dom4.org>, "
524                + "\"big \\\"G\\\"\" <bigG@dom5.net>, "
525                + "=?UTF-8?B?5pel5pys6Kqe?= <address6@co.jp>, "
526                + "=?UTF-8?B?8J2MgfCdjYY=?= <address8@ne.jp>",
527                line);
528    }
529
530    /**
531     * Test various combinations of the toFriendly (single) method
532     */
533    public void testToFriendlySingle() {
534        assertEquals("personal1", mAddress1.toFriendly());
535        assertEquals("address2", mAddress2.toFriendly());
536        assertEquals("address3", mAddress3.toFriendly());
537    }
538
539    /**
540     * Test various combinations of the toFriendly (array) method
541     */
542    public void testToFriendlyArray() {
543        Address[] list1 = null;
544        Address[] list2 = new Address[0];
545        Address[] list3 = new Address[] { mAddress1 };
546        Address[] list4 = new Address[] { mAddress1, mAddress2, mAddress3 };
547
548        assertEquals(null, Address.toFriendly(list1));
549        assertEquals(null, Address.toFriendly(list2));
550        assertEquals("personal1", Address.toFriendly(list3));
551        assertEquals("personal1, address2, address3", Address.toFriendly(list4));
552    }
553
554    /**
555     * Simple quick checks of empty-input edge conditions for pack()
556     *
557     * NOTE:  This is not a claim that these edge cases are "correct", only to maintain consistent
558     * behavior while I am changing some of the code in the function under test.
559     */
560    public void testEmptyPack() {
561        String result;
562
563        // null input => null string
564        result = Address.pack(null);
565        assertNull("packing null", result);
566
567        // zero-length input => empty string
568        result = Address.pack(new Address[] { });
569        assertEquals("packing empty array", "", result);
570    }
571
572    /**
573     * Simple quick checks of empty-input edge conditions for unpack()
574     *
575     * NOTE:  This is not a claim that these edge cases are "correct", only to maintain consistent
576     * behavior while I am changing some of the code in the function under test.
577     */
578    public void testEmptyUnpack() {
579        Address[] result;
580
581        // null input => empty array
582        result = Address.unpack(null);
583        assertTrue("unpacking null address", result != null && result.length == 0);
584
585        // empty string input => empty array
586        result = Address.unpack("");
587        assertTrue("unpacking zero-length", result != null && result.length == 0);
588    }
589
590    private static boolean addressEquals(Address a1, Address a2) {
591        if (!a1.equals(a2)) {
592            return false;
593        }
594        final String displayName1 = a1.getPersonal();
595        final String displayName2 = a2.getPersonal();
596        if (displayName1 == null) {
597            return displayName2 == null;
598        } else {
599            return displayName1.equals(displayName2);
600        }
601    }
602
603    private static boolean addressArrayEquals(Address[] array1, Address[] array2) {
604        if (array1.length != array2.length) {
605            return false;
606        }
607        for (int i = array1.length - 1; i >= 0; --i) {
608            if (!addressEquals(array1[i], array2[i])) {
609                return false;
610            }
611        }
612        return true;
613    }
614
615    public void testPackUnpack() {
616        for (Address[] list : PACK_CASES) {
617            String packed = Address.pack(list);
618            assertTrue(packed, addressArrayEquals(list, Address.unpack(packed)));
619        }
620    }
621
622    /**
623     * Tests that unpackToString() returns the same result as toString(unpack()).
624     */
625    public void testUnpackToString() {
626        assertNull(Address.unpackToString(null));
627        assertNull(Address.unpackToString(""));
628
629        for (Address[] list : PACK_CASES) {
630            String packed = Address.pack(list);
631            String s1 = Address.unpackToString(packed);
632            String s2 = Address.toString(Address.unpack(packed));
633            assertEquals(s2, s2, s1);
634        }
635    }
636
637    /**
638     * Tests that parseAndPack() returns the same result as pack(parse()).
639     */
640    public void testParseAndPack() {
641        String s1 = Address.parseAndPack(MULTI_ADDRESSES_LIST);
642        String s2 = Address.pack(Address.parse(MULTI_ADDRESSES_LIST));
643        assertEquals(s2, s1);
644    }
645
646    public void testSinglePack() {
647        Address[] addrArray = new Address[1];
648        for (Address address : new Address[]{PACK_ADDR_1, PACK_ADDR_2, PACK_ADDR_3}) {
649            String packed1 = address.pack();
650            addrArray[0] = address;
651            String packed2 = Address.pack(addrArray);
652            assertEquals(packed1, packed2);
653        }
654    }
655
656    /**
657     * Tests that:
658     * 1. unpackFirst() with empty list returns null.
659     * 2. unpackFirst() with non-empty returns the same as unpack()[0]
660     */
661    public void testUnpackFirst() {
662        assertNull(Address.unpackFirst(null));
663        assertNull(Address.unpackFirst(""));
664
665        for (Address[] list : PACK_CASES) {
666            String packed = Address.pack(list);
667            Address[] array = Address.unpack(packed);
668            Address first = Address.unpackFirst(packed);
669            assertTrue(packed, addressEquals(array[0], first));
670        }
671    }
672
673    public void testIsValidAddress() {
674        String notValid[] = {"", "foo", "john@", "x@y", "x@y.", "foo.com"};
675        String valid[] = {"x@y.z", "john@gmail.com", "a@b.c.d"};
676        for (String address : notValid) {
677            assertTrue(address, !Address.isValidAddress(address));
678        }
679        for (String address : valid) {
680            assertTrue(address, Address.isValidAddress(address));
681        }
682
683        // isAllValid() must accept empty address list as valid
684        assertTrue("Empty address list is valid", Address.isAllValid(""));
685    }
686
687    /**
688     * Legacy pack() used for testing legacyUnpack().
689     * The packed list is a comma separated list of:
690     * URLENCODE(address)[;URLENCODE(personal)]
691     * @See pack()
692     */
693    private static String legacyPack(Address[] addresses) {
694        if (addresses == null) {
695            return null;
696        } else if (addresses.length == 0) {
697            return "";
698        }
699        StringBuffer sb = new StringBuffer();
700        for (int i = 0, count = addresses.length; i < count; i++) {
701            Address address = addresses[i];
702            try {
703                sb.append(URLEncoder.encode(address.getAddress(), "UTF-8"));
704                if (address.getPersonal() != null) {
705                    sb.append(';');
706                    sb.append(URLEncoder.encode(address.getPersonal(), "UTF-8"));
707                }
708                if (i < count - 1) {
709                    sb.append(',');
710                }
711            }
712            catch (UnsupportedEncodingException uee) {
713                return null;
714            }
715        }
716        return sb.toString();
717    }
718}
719