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 import java.io.UnsupportedEncodingException;
     24 import 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
     31 public class AddressUnitTests extends AndroidTestCase {
     32 
     33     private static final String MULTI_ADDRESSES_LIST =
     34             "noname1 (at) dom1.com, "
     35             + "<noname2 (at) dom2.com>, "
     36             + "simple name <address3 (at) dom3.org>, "
     37             + "\"name,4\" <address4 (at) dom4.org>,"
     38             + "\"big \\\"G\\\"\" <bigG (at) dom5.net>,"
     39             + "\u65E5\u672C\u8A9E <address6 (at) co.jp>,"
     40             + "\"\u65E5\u672C\u8A9E\" <address7 (at) co.jp>,"
     41             + "\uD834\uDF01\uD834\uDF46 <address8 (at) ne.jp>,"
     42             + "\"\uD834\uDF01\uD834\uDF46\" <address9 (at) ne.jp>,"
     43             + "noname (at) dom.com <noname (at) 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 (at) gmail.com", "John Doe");
     48     private static final Address PACK_ADDR_2 = new Address("foo (at) bar.com", null);
     49     private static final Address PACK_ADDR_3 = new Address(
     50             "mar.y+test (at) 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 (at) dom1.com";
    100         String bracketAddress = "<user2 (at) dom2.com>";
    101 
    102         Address address = new Address(bareAddress);
    103         assertEquals("bare address", "user1 (at) dom1.com", address.getAddress());
    104 
    105         address.setAddress(bracketAddress);
    106         assertEquals("bracket address", "user2 (at) dom2.com", address.getAddress());
    107     }
    108 
    109     /**
    110      * Test for empty setPersonal().
    111      */
    112     public void testNullPersonal() {
    113         Address address = new Address("user1 (at) 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 (at) 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 (at) 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 (at) dom1.com");
    199         assertEquals("bare address count", 1, address1.length);
    200         assertEquals("bare address", "address1 (at) dom1.com", address1[0].getAddress());
    201         assertNull("name of bare address", address1[0].getPersonal());
    202 
    203         Address[] address2 = Address.parse("<address2 (at) dom2.com>");
    204         assertEquals("bracket address count", 1, address2.length);
    205         assertEquals("bracket address", "address2 (at) dom2.com", address2[0].getAddress());
    206         assertNull("name of bracket address", address2[0].getPersonal());
    207 
    208         Address[] address3 = Address.parse("first last <address3 (at) dom3.org>");
    209         assertEquals("address with name count", 1, address3.length);
    210         assertEquals("address with name", "address3 (at) 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 (at) dom4.org>");
    214         assertEquals("address with quoted name count", 1, address4.length);
    215         assertEquals("address with quoted name", "address4 (at) 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 (at) 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 (at) dom6.com.");
    239         assertEquals("domain ends with dot", 0, address6.length);
    240 
    241         Address[] address7 = Address.parse("address7 (at) .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 (at) dom1.net, <address2 (at) dom2.com>");
    250         assertEquals("address count", 2, addresses.length);
    251 
    252         assertEquals("bare address", "address1 (at) dom1.net", addresses[0].getAddress());
    253         assertNull("bare address name", addresses[0].getPersonal());
    254 
    255         assertEquals("bracket address", "address2 (at) 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 (at) dom1.net>, " +
    265                 "\"name,2\" <address2 (at) dom2.org>");
    266         assertEquals("address count", 2, addresses.length);
    267 
    268         assertEquals("bare name address", "address1 (at) dom1.net", addresses[0].getAddress());
    269         assertEquals("bare name", "name 1", addresses[0].getPersonal());
    270 
    271         assertEquals("double quoted name address", "address2 (at) 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 (at) dom1.jp>, " +
    281                 "\"\u3042\u3044\u3046,\u3048\u304A\" <address2 (at) dom2.jp>");
    282         assertEquals("address count", 2, addresses.length);
    283 
    284         assertEquals("bare utf-16 name address", "address1 (at) 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 (at) 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 (at) dom1.net>, " +
    300                 "\"\uD834\uDF01\uD834\uDF46,\uD834\uDF22\" <address2 (at) dom2.com>");
    301         assertEquals("address count", 2, addresses.length);
    302 
    303         assertEquals("bare utf-32 name address", "address1 (at) 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 (at) 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 (at) dom1.com", addresses[0].getAddress());
    322         assertNull("no name 1 name", addresses[0].getPersonal());
    323         assertEquals("no name 2 address", "noname2 (at) dom2.com", addresses[1].getAddress());
    324         assertNull("no name 2 name", addresses[1].getPersonal());
    325         assertEquals("simple name address", "address3 (at) dom3.org", addresses[2].getAddress());
    326         assertEquals("simple name name", "simple name", addresses[2].getPersonal());
    327         assertEquals("double quoted name address", "address4 (at) dom4.org", addresses[3].getAddress());
    328         assertEquals("double quoted name name", "name,4", addresses[3].getPersonal());
    329         assertEquals("quoted name address", "bigG (at) dom5.net", addresses[4].getAddress());
    330         assertEquals("quoted name name", "big \"G\"", addresses[4].getPersonal());
    331         assertEquals("utf-16 name address", "address6 (at) 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 (at) 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 (at) 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 (at) 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 (at) dom1.com", addresses[0].toString());
    353         assertEquals("no name 2", "noname2 (at) dom2.com", addresses[1].toString());
    354         assertEquals("simple name", "simple name <address3 (at) dom3.org>", addresses[2].toString());
    355         assertEquals("double quoted name",
    356                 "\"name,4\" <address4 (at) dom4.org>", addresses[3].toString());
    357         assertEquals("quoted name", "\"big \"G\"\" <bigG (at) dom5.net>", addresses[4].toString());
    358         assertEquals("utf-16 name", "\u65E5\u672C\u8A9E <address6 (at) co.jp>",
    359                 addresses[5].toString());
    360         assertEquals("utf-16 quoted name", "\u65E5\u672C\u8A9E <address7 (at) co.jp>",
    361                 addresses[6].toString());
    362         assertEquals("utf-32 name", "\uD834\uDF01\uD834\uDF46 <address8 (at) ne.jp>",
    363                 addresses[7].toString());
    364         assertEquals("utf-32 quoted name", "\uD834\uDF01\uD834\uDF46 <address9 (at) ne.jp>",
    365                 addresses[8].toString());
    366         assertEquals("name==address", "noname (at) 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 (at) 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 (at) dom1.com",
    382                     line);
    383         }
    384         {
    385             String line = Address.toString(addresses);
    386             assertEquals("toString multi-n",
    387                     "noname1 (at) dom1.com,"
    388                     + "noname2 (at) dom2.com,"
    389                     + "simple name <address3 (at) dom3.org>,"
    390                     + "\"name,4\" <address4 (at) dom4.org>,"
    391                     + "\"big \"G\"\" <bigG (at) dom5.net>,"
    392                     + "\u65E5\u672C\u8A9E <address6 (at) co.jp>,"
    393                     + "\u65E5\u672C\u8A9E <address7 (at) co.jp>,"
    394                     + "\uD834\uDF01\uD834\uDF46 <address8 (at) ne.jp>,"
    395                     + "\uD834\uDF01\uD834\uDF46 <address9 (at) ne.jp>,"
    396                     + "noname (at) dom.com",
    397                     line);
    398         }
    399 
    400         // With custom separator
    401         {
    402             String line = Address.toString(address, "$");
    403             assertEquals("toString multi-1",
    404                     "noname1 (at) dom1.com",
    405                     line);
    406         }
    407 
    408         {
    409             String line = Address.toString(addresses, "$");
    410             assertEquals("toString multi-n",
    411                     "noname1 (at) dom1.com$"
    412                     + "noname2 (at) dom2.com$"
    413                     + "simple name <address3 (at) dom3.org>$"
    414                     + "\"name,4\" <address4 (at) dom4.org>$"
    415                     + "\"big \"G\"\" <bigG (at) dom5.net>$"
    416                     + "\u65E5\u672C\u8A9E <address6 (at) co.jp>$"
    417                     + "\u65E5\u672C\u8A9E <address7 (at) co.jp>$"
    418                     + "\uD834\uDF01\uD834\uDF46 <address8 (at) ne.jp>$"
    419                     + "\uD834\uDF01\uD834\uDF46 <address9 (at) ne.jp>$"
    420                     + "noname (at) 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 (at) dom1.com>, =?UTF-8?B?5pel5pys6Kqe?= <address2 (at) co.jp>");
    431 
    432         assertEquals("address count", 2, addresses.length);
    433 
    434         assertEquals("quoted name address", "bigG (at) dom1.com", addresses[0].getAddress());
    435         assertEquals("quoted name", "big \"G\"", addresses[0].getPersonal());
    436 
    437         assertEquals("encoded name address", "address2 (at) 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 (at) dom1.com");
    446         Address noName2 = new Address("<noname2 (at) dom2.com>", "");
    447         Address simpleName = new Address("address3 (at) dom3.org", "simple name");
    448         Address dquoteName = new Address("address4 (at) dom4.org", "name,4");
    449         Address quotedName = new Address("bigG (at) dom5.net", "big \"G\"");
    450         Address utf16Name = new Address("<address6 (at) co.jp>", "\"\u65E5\u672C\u8A9E\"");
    451         Address utf32Name = new Address("<address8 (at) ne.jp>", "\uD834\uDF01\uD834\uDF46");
    452         Address sameName = new Address("address (at) dom.org", "address (at) dom.org");
    453 
    454         // test for internal states.
    455         assertEquals("no name 1 address", "noname1 (at) dom1.com", noName1.getAddress());
    456         assertNull("no name 1 name", noName1.getPersonal());
    457         assertEquals("no name 2 address", "noname2 (at) dom2.com", noName2.getAddress());
    458         assertNull("no name 2 name", noName2.getPersonal());
    459         assertEquals("simple name address", "address3 (at) dom3.org", simpleName.getAddress());
    460         assertEquals("simple name name", "simple name", simpleName.getPersonal());
    461         assertEquals("double quoted name address", "address4 (at) dom4.org", dquoteName.getAddress());
    462         assertEquals("double quoted name name", "name,4", dquoteName.getPersonal());
    463         assertEquals("quoted name address", "bigG (at) dom5.net", quotedName.getAddress());
    464         assertEquals("quoted name name", "big \"G\"", quotedName.getPersonal());
    465         assertEquals("utf-16 name address", "address6 (at) co.jp", utf16Name.getAddress());
    466         assertEquals("utf-16 name name", "\u65E5\u672C\u8A9E", utf16Name.getPersonal());
    467         assertEquals("utf-32 name address", "address8 (at) ne.jp", utf32Name.getAddress());
    468         assertEquals("utf-32 name name", "\uD834\uDF01\uD834\uDF46", utf32Name.getPersonal());
    469         assertEquals("name == address address", "address (at) dom.org", sameName.getAddress());
    470         assertEquals("name == address name", "address (at) dom.org", sameName.getPersonal());
    471 
    472         // Test for toHeader() results.
    473         assertEquals("no name 1", "noname1 (at) dom1.com", noName1.toHeader());
    474         assertEquals("no name 2", "noname2 (at) dom2.com", noName2.toHeader());
    475         assertEquals("simple name", "simple name <address3 (at) dom3.org>", simpleName.toHeader());
    476         assertEquals("double quoted name", "\"name,4\" <address4 (at) dom4.org>", dquoteName.toHeader());
    477         assertEquals("quoted name", "\"big \\\"G\\\"\" <bigG (at) dom5.net>", quotedName.toHeader());
    478         assertEquals("utf-16 name", "=?UTF-8?B?5pel5pys6Kqe?= <address6 (at) co.jp>",
    479                 utf16Name.toHeader());
    480         assertEquals("utf-32 name", "=?UTF-8?B?8J2MgfCdjYY=?= <address8 (at) ne.jp>",
    481                 utf32Name.toHeader());
    482         assertEquals("name == address", "\"address (at) dom.org\" <address (at) 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 (at) dom1.com");
    491         Address noName2 = new Address("<noname2 (at) dom2.com>", "");
    492         Address simpleName = new Address("address3 (at) dom3.org", "simple name");
    493         Address dquoteName = new Address("address4 (at) dom4.org", "name,4");
    494         Address quotedName = new Address("bigG (at) dom5.net", "big \"G\"");
    495         Address utf16Name = new Address("<address6 (at) co.jp>", "\"\u65E5\u672C\u8A9E\"");
    496         Address utf32Name = new Address("<address8 (at) ne.jp>", "\uD834\uDF01\uD834\uDF46");
    497 
    498         // test for internal states.
    499         assertEquals("no name 1 address", "noname1 (at) dom1.com", noName1.getAddress());
    500         assertNull("no name 1 name", noName1.getPersonal());
    501         assertEquals("no name 2 address", "noname2 (at) dom2.com", noName2.getAddress());
    502         assertNull("no name 2 name", noName2.getPersonal());
    503         assertEquals("simple name address", "address3 (at) dom3.org", simpleName.getAddress());
    504         assertEquals("simple name name", "simple name", simpleName.getPersonal());
    505         assertEquals("double quoted name address", "address4 (at) dom4.org", dquoteName.getAddress());
    506         assertEquals("double quoted name name", "name,4", dquoteName.getPersonal());
    507         assertEquals("quoted name address", "bigG (at) dom5.net", quotedName.getAddress());
    508         assertEquals("quoted name name", "big \"G\"", quotedName.getPersonal());
    509         assertEquals("utf-16 name address", "address6 (at) co.jp", utf16Name.getAddress());
    510         assertEquals("utf-16 name name", "\u65E5\u672C\u8A9E", utf16Name.getPersonal());
    511         assertEquals("utf-32 name address", "address8 (at) 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 (at) dom1.com, "
    521                 + "noname2 (at) dom2.com, "
    522                 + "simple name <address3 (at) dom3.org>, "
    523                 + "\"name,4\" <address4 (at) dom4.org>, "
    524                 + "\"big \\\"G\\\"\" <bigG (at) dom5.net>, "
    525                 + "=?UTF-8?B?5pel5pys6Kqe?= <address6 (at) co.jp>, "
    526                 + "=?UTF-8?B?8J2MgfCdjYY=?= <address8 (at) 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 (at) y.z", "john (at) gmail.com", "a (at) 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