Home | History | Annotate | Download | only in mail
      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 (at) dom1.com, "
     32                     + "<noname2 (at) dom2.com>, "
     33                     + "simple name <address3 (at) dom3.org>, "
     34                     + "\"name,4\" <address4 (at) dom4.org>,"
     35                     + "\"big \\\"G\\\"\" <bigG (at) dom5.net>,"
     36                     + "\u65E5\u672C\u8A9E <address6 (at) co.jp>,"
     37                     + "\"\u65E5\u672C\u8A9E\" <address7 (at) co.jp>,"
     38                     + "\uD834\uDF01\uD834\uDF46 <address8 (at) ne.jp>,"
     39                     + "\"\uD834\uDF01\uD834\uDF46\" <address9 (at) ne.jp>,"
     40                     + "noname (at) dom.com <noname (at) 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 (at) gmail.com", "John Doe");
     45     private static final Address ADDR_2 = new Address("foo (at) bar.com", null);
     46     private static final Address ADDR_3 = new Address("mar.y+test (at) 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
     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
     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      */
     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
     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
    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      */
    112     public void testSetAddress() {
    113         String bareAddress = "user1 (at) dom1.com";
    114         String bracketAddress = "<user2 (at) dom2.com>";
    115 
    116         Address address = new Address(bareAddress);
    117         assertEquals("bare address", "user1 (at) dom1.com", address.getAddress());
    118 
    119         address.setAddress(bracketAddress);
    120         assertEquals("bracket address", "user2 (at) dom2.com", address.getAddress());
    121     }
    122 
    123     /**
    124      * Test for empty setPersonal().
    125      */
    126     public void brokentestNullPersonal() {
    127         Address address = new Address("user1 (at) 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      */
    143     public void brokentestSetPersonal() {
    144         Address address = new Address("user1 (at) 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      */
    160     public void brokentestSetPersonalMultipleEncodings() {
    161         Address address = new Address("user1 (at) 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      */
    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      */
    211     public void testSingleParse() {
    212         Address[] address1 = Address.parse("address1 (at) dom1.com");
    213         assertEquals("bare address count", 1, address1.length);
    214         assertEquals("bare address", "address1 (at) dom1.com", address1[0].getAddress());
    215         assertNull("name of bare address", address1[0].getPersonal());
    216 
    217         Address[] address2 = Address.parse("<address2 (at) dom2.com>");
    218         assertEquals("bracket address count", 1, address2.length);
    219         assertEquals("bracket address", "address2 (at) dom2.com", address2[0].getAddress());
    220         assertNull("name of bracket address", address2[0].getPersonal());
    221 
    222         Address[] address3 = Address.parse("first last <address3 (at) dom3.org>");
    223         assertEquals("address with name count", 1, address3.length);
    224         assertEquals("address with name", "address3 (at) 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 (at) dom4.org>");
    228         assertEquals("address with quoted name count", 1, address4.length);
    229         assertEquals("address with quoted name", "address4 (at) 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      */
    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 (at) 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 (at) dom6.com.");
    253         assertEquals("domain ends with dot", 0, address6.length);
    254 
    255         Address[] address7 = Address.parse("address7 (at) .dom7.org");
    256         assertEquals("domain starts with dot", 0, address7.length);
    257     }
    258 
    259     /**
    260      * Test parsing for address part.
    261      */
    262     public void testParsingAddress() {
    263         Address[] addresses = Address.parse("address1 (at) dom1.net, <address2 (at) dom2.com>");
    264         assertEquals("address count", 2, addresses.length);
    265 
    266         assertEquals("bare address", "address1 (at) dom1.net", addresses[0].getAddress());
    267         assertNull("bare address name", addresses[0].getPersonal());
    268 
    269         assertEquals("bracket address", "address2 (at) dom2.com", addresses[1].getAddress());
    270         assertNull("bracket address name", addresses[1].getPersonal());
    271     }
    272 
    273     /**
    274      * Test parsing for simple name part.
    275      */
    276     public void testParsingSimpleName() {
    277         Address[] addresses = Address.parse(
    278                 "name 1 <address1 (at) dom1.net>, " +
    279                         "\"name,2\" <address2 (at) dom2.org>");
    280         assertEquals("address count", 2, addresses.length);
    281 
    282         assertEquals("bare name address", "address1 (at) dom1.net", addresses[0].getAddress());
    283         assertEquals("bare name", "name 1", addresses[0].getPersonal());
    284 
    285         assertEquals("double quoted name address", "address2 (at) 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      */
    292     public void testParsingUtf16Name() {
    293         Address[] addresses = Address.parse(
    294                 "\u3042\u3044\u3046 \u3048\u304A <address1 (at) dom1.jp>, " +
    295                         "\"\u3042\u3044\u3046,\u3048\u304A\" <address2 (at) dom2.jp>");
    296         assertEquals("address count", 2, addresses.length);
    297 
    298         assertEquals("bare utf-16 name address", "address1 (at) 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 (at) 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      */
    311     public void testParsingUtf32Name() {
    312         Address[] addresses = Address.parse(
    313                 "\uD834\uDF01\uD834\uDF46 \uD834\uDF22 <address1 (at) dom1.net>, " +
    314                         "\"\uD834\uDF01\uD834\uDF46,\uD834\uDF22\" <address2 (at) dom2.com>");
    315         assertEquals("address count", 2, addresses.length);
    316 
    317         assertEquals("bare utf-32 name address", "address1 (at) 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 (at) 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      */
    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 (at) dom1.com", addresses[0].getAddress());
    336         assertNull("no name 1 name", addresses[0].getPersonal());
    337         assertEquals("no name 2 address", "noname2 (at) dom2.com", addresses[1].getAddress());
    338         assertNull("no name 2 name", addresses[1].getPersonal());
    339         assertEquals("simple name address", "address3 (at) dom3.org", addresses[2].getAddress());
    340         assertEquals("simple name name", "simple name", addresses[2].getPersonal());
    341         assertEquals("double quoted name address", "address4 (at) dom4.org", addresses[3].getAddress());
    342         assertEquals("double quoted name name", "name,4", addresses[3].getPersonal());
    343         assertEquals("quoted name address", "bigG (at) dom5.net", addresses[4].getAddress());
    344         assertEquals("quoted name name", "big \"G\"", addresses[4].getPersonal());
    345         assertEquals("utf-16 name address", "address6 (at) 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 (at) 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 (at) 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 (at) 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      */
    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 (at) dom1.com", addresses[0].toString());
    367         assertEquals("no name 2", "noname2 (at) dom2.com", addresses[1].toString());
    368         assertEquals("simple name", "simple name <address3 (at) dom3.org>", addresses[2].toString());
    369         assertEquals("double quoted name",
    370                 "\"name,4\" <address4 (at) dom4.org>", addresses[3].toString());
    371         assertEquals("quoted name", "\"big \"G\"\" <bigG (at) dom5.net>", addresses[4].toString());
    372         assertEquals("utf-16 name", "\u65E5\u672C\u8A9E <address6 (at) co.jp>",
    373                 addresses[5].toString());
    374         assertEquals("utf-16 quoted name", "\u65E5\u672C\u8A9E <address7 (at) co.jp>",
    375                 addresses[6].toString());
    376         assertEquals("utf-32 name", "\uD834\uDF01\uD834\uDF46 <address8 (at) ne.jp>",
    377                 addresses[7].toString());
    378         assertEquals("utf-32 quoted name", "\uD834\uDF01\uD834\uDF46 <address9 (at) ne.jp>",
    379                 addresses[8].toString());
    380         assertEquals("name==address", "noname (at) dom.com", addresses[9].toString());
    381     }
    382 
    383     /**
    384      * Test various combinations of the toString (multi) method
    385      */
    386     public void testToStringMulti() {
    387         final Address[] address = Address.parse("noname1 (at) 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 (at) dom1.com",
    396                     line);
    397         }
    398         {
    399             String line = Address.toString(addresses);
    400             assertEquals("toString multi-n",
    401                     "noname1 (at) dom1.com,"
    402                             + "noname2 (at) dom2.com,"
    403                             + "simple name <address3 (at) dom3.org>,"
    404                             + "\"name,4\" <address4 (at) dom4.org>,"
    405                             + "\"big \"G\"\" <bigG (at) dom5.net>,"
    406                             + "\u65E5\u672C\u8A9E <address6 (at) co.jp>,"
    407                             + "\u65E5\u672C\u8A9E <address7 (at) co.jp>,"
    408                             + "\uD834\uDF01\uD834\uDF46 <address8 (at) ne.jp>,"
    409                             + "\uD834\uDF01\uD834\uDF46 <address9 (at) ne.jp>,"
    410                             + "noname (at) dom.com",
    411                     line);
    412         }
    413 
    414         // With custom separator
    415         {
    416             String line = Address.toString(address, "$");
    417             assertEquals("toString multi-1",
    418                     "noname1 (at) dom1.com",
    419                     line);
    420         }
    421 
    422         {
    423             String line = Address.toString(addresses, "$");
    424             assertEquals("toString multi-n",
    425                     "noname1 (at) dom1.com$"
    426                             + "noname2 (at) dom2.com$"
    427                             + "simple name <address3 (at) dom3.org>$"
    428                             + "\"name,4\" <address4 (at) dom4.org>$"
    429                             + "\"big \"G\"\" <bigG (at) dom5.net>$"
    430                             + "\u65E5\u672C\u8A9E <address6 (at) co.jp>$"
    431                             + "\u65E5\u672C\u8A9E <address7 (at) co.jp>$"
    432                             + "\uD834\uDF01\uD834\uDF46 <address8 (at) ne.jp>$"
    433                             + "\uD834\uDF01\uD834\uDF46 <address9 (at) ne.jp>$"
    434                             + "noname (at) dom.com",
    435                     line);
    436         }
    437     }
    438 
    439     /**
    440      * Test parsing for quoted and encoded name part.
    441      */
    442     public void testParsingQuotedEncodedName() {
    443         Address[] addresses = Address.parse(
    444                 "\"big \\\"G\\\"\" <bigG (at) dom1.com>, =?UTF-8?B?5pel5pys6Kqe?= <address2 (at) co.jp>");
    445 
    446         assertEquals("address count", 2, addresses.length);
    447 
    448         assertEquals("quoted name address", "bigG (at) dom1.com", addresses[0].getAddress());
    449         assertEquals("quoted name", "big \"G\"", addresses[0].getPersonal());
    450 
    451         assertEquals("encoded name address", "address2 (at) 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      */
    458     public void testToHeaderSingle() {
    459         Address noName1 = new Address("noname1 (at) dom1.com");
    460         Address noName2 = new Address("<noname2 (at) dom2.com>", "");
    461         Address simpleName = new Address("address3 (at) dom3.org", "simple name");
    462         Address dquoteName = new Address("address4 (at) dom4.org", "name,4");
    463         Address quotedName = new Address("bigG (at) dom5.net", "big \"G\"");
    464         Address utf16Name = new Address("<address6 (at) co.jp>", "\"\u65E5\u672C\u8A9E\"");
    465         Address utf32Name = new Address("<address8 (at) ne.jp>", "\uD834\uDF01\uD834\uDF46");
    466         Address sameName = new Address("address (at) dom.org", "address (at) dom.org");
    467 
    468         // test for internal states.
    469         assertEquals("no name 1 address", "noname1 (at) dom1.com", noName1.getAddress());
    470         assertNull("no name 1 name", noName1.getPersonal());
    471         assertEquals("no name 2 address", "noname2 (at) dom2.com", noName2.getAddress());
    472         assertNull("no name 2 name", noName2.getPersonal());
    473         assertEquals("simple name address", "address3 (at) dom3.org", simpleName.getAddress());
    474         assertEquals("simple name name", "simple name", simpleName.getPersonal());
    475         assertEquals("double quoted name address", "address4 (at) dom4.org", dquoteName.getAddress());
    476         assertEquals("double quoted name name", "name,4", dquoteName.getPersonal());
    477         assertEquals("quoted name address", "bigG (at) dom5.net", quotedName.getAddress());
    478         assertEquals("quoted name name", "big \"G\"", quotedName.getPersonal());
    479         assertEquals("utf-16 name address", "address6 (at) co.jp", utf16Name.getAddress());
    480         assertEquals("utf-16 name name", "\u65E5\u672C\u8A9E", utf16Name.getPersonal());
    481         assertEquals("utf-32 name address", "address8 (at) ne.jp", utf32Name.getAddress());
    482         assertEquals("utf-32 name name", "\uD834\uDF01\uD834\uDF46", utf32Name.getPersonal());
    483         assertEquals("name == address address", "address (at) dom.org", sameName.getAddress());
    484         assertEquals("name == address name", "address (at) dom.org", sameName.getPersonal());
    485 
    486         // Test for toHeader() results.
    487         assertEquals("no name 1", "noname1 (at) dom1.com", noName1.toHeader());
    488         assertEquals("no name 2", "noname2 (at) dom2.com", noName2.toHeader());
    489         assertEquals("simple name", "simple name <address3 (at) dom3.org>", simpleName.toHeader());
    490         assertEquals("double quoted name", "\"name,4\" <address4 (at) dom4.org>", dquoteName.toHeader());
    491         assertEquals("quoted name", "\"big \\\"G\\\"\" <bigG (at) dom5.net>", quotedName.toHeader());
    492         assertEquals("utf-16 name", "=?UTF-8?B?5pel5pys6Kqe?= <address6 (at) co.jp>",
    493                 utf16Name.toHeader());
    494         assertEquals("utf-32 name", "=?UTF-8?B?8J2MgfCdjYY=?= <address8 (at) ne.jp>",
    495                 utf32Name.toHeader());
    496         assertEquals("name == address", "\"address (at) dom.org\" <address (at) dom.org>",
    497                 sameName.toHeader());
    498     }
    499 
    500     /**
    501      * Test various combinations of the toHeader (multi) method
    502      */
    503     public void testToHeaderMulti() {
    504         Address noName1 = new Address("noname1 (at) dom1.com");
    505         Address noName2 = new Address("<noname2 (at) dom2.com>", "");
    506         Address simpleName = new Address("address3 (at) dom3.org", "simple name");
    507         Address dquoteName = new Address("address4 (at) dom4.org", "name,4");
    508         Address quotedName = new Address("bigG (at) dom5.net", "big \"G\"");
    509         Address utf16Name = new Address("<address6 (at) co.jp>", "\"\u65E5\u672C\u8A9E\"");
    510         Address utf32Name = new Address("<address8 (at) ne.jp>", "\uD834\uDF01\uD834\uDF46");
    511 
    512         // test for internal states.
    513         assertEquals("no name 1 address", "noname1 (at) dom1.com", noName1.getAddress());
    514         assertNull("no name 1 name", noName1.getPersonal());
    515         assertEquals("no name 2 address", "noname2 (at) dom2.com", noName2.getAddress());
    516         assertNull("no name 2 name", noName2.getPersonal());
    517         assertEquals("simple name address", "address3 (at) dom3.org", simpleName.getAddress());
    518         assertEquals("simple name name", "simple name", simpleName.getPersonal());
    519         assertEquals("double quoted name address", "address4 (at) dom4.org", dquoteName.getAddress());
    520         assertEquals("double quoted name name", "name,4", dquoteName.getPersonal());
    521         assertEquals("quoted name address", "bigG (at) dom5.net", quotedName.getAddress());
    522         assertEquals("quoted name name", "big \"G\"", quotedName.getPersonal());
    523         assertEquals("utf-16 name address", "address6 (at) co.jp", utf16Name.getAddress());
    524         assertEquals("utf-16 name name", "\u65E5\u672C\u8A9E", utf16Name.getPersonal());
    525         assertEquals("utf-32 name address", "address8 (at) 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 (at) dom1.com, "
    535                         + "noname2 (at) dom2.com, "
    536                         + "simple name <address3 (at) dom3.org>, "
    537                         + "\"name,4\" <address4 (at) dom4.org>, "
    538                         + "\"big \\\"G\\\"\" <bigG (at) dom5.net>, "
    539                         + "=?UTF-8?B?5pel5pys6Kqe?= <address6 (at) co.jp>, "
    540                         + "=?UTF-8?B?8J2MgfCdjYY=?= <address8 (at) ne.jp>",
    541                 line);
    542     }
    543 
    544     /**
    545      * Test various combinations of the toFriendly (single) method
    546      */
    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      */
    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      */
    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      */
    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 
    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 
    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 
    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      */
    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      */
    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 
    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      */
    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 
    688     public void testIsValidAddress() {
    689         String notValid[] = {"", "foo", "john@", "x@y", "x@y.", "foo.com"};
    690         String valid[] = {"x (at) y.z", "john (at) gmail.com", "a (at) 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