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