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.utility; 18 19 import android.content.Context; 20 import android.database.Cursor; 21 import android.database.CursorWrapper; 22 import android.net.Uri; 23 import android.os.Environment; 24 import android.test.AndroidTestCase; 25 import android.test.MoreAsserts; 26 import android.test.suitebuilder.annotation.SmallTest; 27 import android.test.suitebuilder.annotation.Suppress; 28 import android.text.SpannableStringBuilder; 29 import android.text.TextUtils; 30 import android.widget.TextView; 31 32 import com.android.email.DBTestHelper; 33 import com.android.email.TestUtils; 34 import com.android.email.provider.ProviderTestUtils; 35 import com.android.emailcommon.provider.Account; 36 import com.android.emailcommon.provider.EmailContent.Attachment; 37 import com.android.emailcommon.provider.Mailbox; 38 import com.android.emailcommon.utility.Utility.NewFileCreator; 39 import com.android.mail.utils.MatrixCursorWithCachedColumns; 40 41 import java.io.File; 42 import java.io.FileNotFoundException; 43 import java.io.FileOutputStream; 44 import java.io.IOException; 45 import java.io.OutputStream; 46 import java.util.ArrayList; 47 import java.util.Collection; 48 import java.util.HashSet; 49 import java.util.Set; 50 51 /** 52 * This is a series of unit tests for the Utility class. These tests must be locally 53 * complete - no server(s) required. 54 * 55 * You can run this entire test case with: 56 * runtest -c com.android.email.UtilityUnitTests email 57 */ 58 @Suppress 59 @SmallTest 60 public class UtilityUnitTests extends AndroidTestCase { 61 62 private static byte[] b(int... array) { 63 return TestUtils.b(array); 64 } 65 66 public void testToUtf8() { 67 assertNull(Utility.toUtf8(null)); 68 MoreAsserts.assertEquals(new byte[] {}, Utility.toUtf8("")); 69 MoreAsserts.assertEquals(b('a'), Utility.toUtf8("a")); 70 MoreAsserts.assertEquals(b('A', 'B', 'C'), Utility.toUtf8("ABC")); 71 MoreAsserts.assertEquals(b(0xE6, 0x97, 0xA5, 0xE6, 0x9C, 0xAC, 0xE8, 0xAA, 0x9E), 72 Utility.toUtf8("\u65E5\u672C\u8A9E")); 73 } 74 75 public void testFromUtf8() { 76 assertNull(Utility.fromUtf8(null)); 77 assertEquals("", Utility.fromUtf8(new byte[] {})); 78 assertEquals("a", Utility.fromUtf8(b('a'))); 79 assertEquals("ABC", Utility.fromUtf8(b('A', 'B', 'C'))); 80 assertEquals("\u65E5\u672C\u8A9E", 81 Utility.fromUtf8(b(0xE6, 0x97, 0xA5, 0xE6, 0x9C, 0xAC, 0xE8, 0xAA, 0x9E))); 82 } 83 84 public void testIsFirstUtf8Byte() { 85 // 1 byte in UTF-8. 86 checkIsFirstUtf8Byte("0"); // First 2 bits: 00 87 checkIsFirstUtf8Byte("A"); // First 2 bits: 01 88 89 checkIsFirstUtf8Byte("\u00A2"); // 2 bytes in UTF-8. 90 checkIsFirstUtf8Byte("\u20AC"); // 3 bytes in UTF-8. 91 checkIsFirstUtf8Byte("\uD852\uDF62"); // 4 bytes in UTF-8. (surrogate pair) 92 } 93 94 private void checkIsFirstUtf8Byte(String aChar) { 95 byte[] bytes = Utility.toUtf8(aChar); 96 assertTrue("0", Utility.isFirstUtf8Byte(bytes[0])); 97 for (int i = 1; i < bytes.length; i++) { 98 assertFalse(Integer.toString(i), Utility.isFirstUtf8Byte(bytes[i])); 99 } 100 } 101 102 public void testByteToHex() { 103 for (int i = 0; i <= 0xFF; i++) { 104 String hex = Utility.byteToHex((byte) i); 105 assertEquals("val=" + i, 2, hex.length()); 106 assertEquals("val=" + i, i, Integer.parseInt(hex, 16)); 107 } 108 } 109 110 public void testReplaceBareLfWithCrlf() { 111 assertEquals("", Utility.replaceBareLfWithCrlf("")); 112 assertEquals("", Utility.replaceBareLfWithCrlf("\r")); 113 assertEquals("\r\n", Utility.replaceBareLfWithCrlf("\r\n")); 114 assertEquals("\r\n", Utility.replaceBareLfWithCrlf("\n")); 115 assertEquals("\r\n\r\n\r\n", Utility.replaceBareLfWithCrlf("\n\n\n")); 116 assertEquals("A\r\nB\r\nC\r\nD", Utility.replaceBareLfWithCrlf("A\nB\r\nC\nD")); 117 } 118 119 public void testGetSmallHash() { 120 assertEquals("1438642069", Utility.getSmallHash("")); 121 assertEquals("1354919068", Utility.getSmallHash("abc")); 122 } 123 124 public void testGetSmallSha1() { 125 byte[] sha1 = new byte[20]; 126 127 // White box test. Not so great, but to make sure it may detect careless mistakes... 128 assertEquals(0, Utility.getSmallHashFromSha1(sha1)); 129 130 for (int i = 0; i < sha1.length; i++) { 131 sha1[i] = (byte) 0xFF; 132 } 133 assertEquals(Integer.MAX_VALUE, Utility.getSmallHashFromSha1(sha1)); 134 135 // Boundary check 136 for (int i = 0; i < 16; i++) { 137 sha1[19] = (byte) i; 138 Utility.getSmallHashFromSha1(sha1); 139 } 140 } 141 142 public void brokentestCleanUpMimeDate() { 143 assertNull(Utility.cleanUpMimeDate(null)); 144 assertEquals("", Utility.cleanUpMimeDate("")); 145 assertEquals("abc", Utility.cleanUpMimeDate("abc")); 146 assertEquals("GMT", Utility.cleanUpMimeDate("GMT")); 147 assertEquals("0000", Utility.cleanUpMimeDate("0000")); 148 assertEquals("-0000", Utility.cleanUpMimeDate("-0000")); 149 assertEquals("+1234", Utility.cleanUpMimeDate("GMT+1234")); 150 assertEquals("-1234", Utility.cleanUpMimeDate("GMT-1234")); 151 assertEquals("gmt-1234", Utility.cleanUpMimeDate("gmt-1234")); 152 assertEquals("GMT-123", Utility.cleanUpMimeDate("GMT-123")); 153 154 assertEquals("Thu, 10 Dec 09 15:08:08 -0700", 155 Utility.cleanUpMimeDate("Thu, 10 Dec 09 15:08:08 GMT-0700")); 156 assertEquals("Thu, 10 Dec 09 15:08:08 -0700", 157 Utility.cleanUpMimeDate("Thu, 10 Dec 09 15:08:08 -0700")); 158 } 159 160 private static class MyNewFileCreator implements NewFileCreator { 161 private final HashSet<String> mExistingFileNames; 162 163 public MyNewFileCreator(String... fileNames) { 164 mExistingFileNames = new HashSet<String>(); 165 for (String f : fileNames) { 166 mExistingFileNames.add(f); 167 } 168 } 169 170 @Override public boolean createNewFile(File f) { 171 return !mExistingFileNames.contains(f.getAbsolutePath()); 172 } 173 } 174 175 public void testCreateUniqueFile() throws Exception { 176 final MyNewFileCreator noFiles = new MyNewFileCreator(); 177 178 // Case 1: Files don't exist. 179 checkCreateUniqueFile("/a", noFiles, "/", "a"); 180 checkCreateUniqueFile("/a.txt", noFiles, "/", "a.txt"); 181 182 checkCreateUniqueFile("/a/b/a", noFiles, "/a/b", "a"); 183 checkCreateUniqueFile("/a/b/a.txt", noFiles, "/a/b", "a.txt"); 184 185 // Case 2: Files exist already. 186 final MyNewFileCreator files = new MyNewFileCreator( 187 "/a", "/a.txt", "/a/b/a", "/a/b/a.txt", 188 "/a-2.txt", 189 "/a/b/a-2", "/a/b/a-3", 190 "/a/b/a-2.txt", "/a/b/a-3.txt", "/a/b/a-4.txt" 191 ); 192 193 checkCreateUniqueFile("/a-2", files, "/", "a"); 194 checkCreateUniqueFile("/a-3.txt", files, "/", "a.txt"); 195 196 checkCreateUniqueFile("/a/b/a-4", files, "/a/b", "a"); 197 checkCreateUniqueFile("/a/b/a-5.txt", files, "/a/b", "a.txt"); 198 } 199 200 private void checkCreateUniqueFile(String expectedFileName, NewFileCreator nfc, 201 String dir, String fileName) throws Exception { 202 assertEquals(expectedFileName, 203 Utility.createUniqueFileInternal(nfc, new File(dir), fileName).toString()); 204 } 205 206 /** 207 * Test that we have the necessary permissions to write to external storage. 208 */ 209 public void testExternalStoragePermissions() throws FileNotFoundException, IOException { 210 File file = null; 211 try { 212 // If there's no storage available, this test is moot 213 if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 214 return; 215 } 216 file = Utility.createUniqueFile(Environment.getExternalStorageDirectory(), 217 "write-test"); 218 OutputStream out = new FileOutputStream(file); 219 out.write(1); 220 out.close(); 221 } finally { 222 try { 223 if (file != null) { 224 if (file.exists()) { 225 file.delete(); 226 } 227 } 228 } catch (Exception e) { 229 // ignore cleanup error - it still throws the original 230 } 231 } 232 } 233 234 public void testIsPortFieldValid() { 235 TextView view = new TextView(getContext()); 236 // null, empty, negative, and non integer strings aren't valid 237 view.setText(null); 238 assertFalse(Utility.isPortFieldValid(view)); 239 view.setText(""); 240 assertFalse(Utility.isPortFieldValid(view)); 241 view.setText("-1"); 242 assertFalse(Utility.isPortFieldValid(view)); 243 view.setText("1403.75"); 244 assertFalse(Utility.isPortFieldValid(view)); 245 view.setText("0"); 246 assertFalse(Utility.isPortFieldValid(view)); 247 view.setText("65536"); 248 assertFalse(Utility.isPortFieldValid(view)); 249 view.setText("i'm not valid"); 250 assertFalse(Utility.isPortFieldValid(view)); 251 // These next values are valid 252 view.setText("1"); 253 assertTrue(Utility.isPortFieldValid(view)); 254 view.setText("65535"); 255 assertTrue(Utility.isPortFieldValid(view)); 256 } 257 258 public void testToPrimitiveLongArray() { 259 assertEquals(0, Utility.toPrimitiveLongArray(createLongCollection()).length); 260 261 final long[] one = Utility.toPrimitiveLongArray(createLongCollection(1)); 262 assertEquals(1, one.length); 263 assertEquals(1, one[0]); 264 265 final long[] two = Utility.toPrimitiveLongArray(createLongCollection(3, 4)); 266 assertEquals(2, two.length); 267 assertEquals(3, two[0]); 268 assertEquals(4, two[1]); 269 } 270 271 public void testToLongSet() { 272 assertEquals(0, Utility.toLongSet(new long[] {}).size()); 273 274 final Set<Long> one = Utility.toLongSet(new long[] {1}); 275 assertEquals(1, one.size()); 276 assertTrue(one.contains(1L)); 277 278 final Set<Long> two = Utility.toLongSet(new long[] {1, 2}); 279 assertEquals(2, two.size()); 280 assertTrue(two.contains(1L)); 281 assertTrue(two.contains(2L)); 282 } 283 284 public void testGetContentFileName() throws Exception { 285 Context providerContext = DBTestHelper.ProviderContextSetupHelper.getProviderContext( 286 mContext); 287 288 final long ACCOUNT_ID = 1; 289 final long MESSAGE_ID = 10; 290 291 Account account = ProviderTestUtils.setupAccount("account", true, providerContext); 292 Mailbox mailbox = ProviderTestUtils.setupMailbox("box", account.mId, true, providerContext); 293 294 // Set up an attachment. 295 Attachment att = ProviderTestUtils.setupAttachment(mailbox.mId, "name", 123, true, 296 providerContext); 297 long attachmentId = att.mId; 298 Uri uri = AttachmentUtilities.getAttachmentUri(account.mId, attachmentId); 299 300 // Case 1: exists in the provider. 301 assertEquals("name", Utility.getContentFileName(providerContext, uri)); 302 303 // Case 2: doesn't exist in the provider 304 Uri notExistUri = AttachmentUtilities.getAttachmentUri(account.mId, 123456789); 305 String lastPathSegment = notExistUri.getLastPathSegment(); 306 assertEquals(lastPathSegment, Utility.getContentFileName(providerContext, notExistUri)); 307 } 308 309 // used by testToPrimitiveLongArray 310 private static Collection<Long> createLongCollection(long... values) { 311 ArrayList<Long> ret = new ArrayList<Long>(); 312 for (long value : values) { 313 ret.add(value); 314 } 315 return ret; 316 } 317 318 public void testDumpCursor() { 319 // Just make sure the method won't crash and returns non-empty string. 320 final Cursor c1 = new MatrixCursorWithCachedColumns(new String[] {"col"}); 321 final Cursor c2 = new CursorWrapper(c1); 322 323 // Note it's a subclass of CursorWrapper. 324 final Cursor c3 = new CursorWrapper(c2) { 325 }; 326 327 assertFalse(TextUtils.isEmpty(Utility.dumpCursor(c1))); 328 assertFalse(TextUtils.isEmpty(Utility.dumpCursor(c2))); 329 assertFalse(TextUtils.isEmpty(Utility.dumpCursor(c3))); 330 assertFalse(TextUtils.isEmpty(Utility.dumpCursor(null))); 331 332 // Test again with closed cursor. 333 c1.close(); 334 assertFalse(TextUtils.isEmpty(Utility.dumpCursor(c1))); 335 assertFalse(TextUtils.isEmpty(Utility.dumpCursor(c2))); 336 assertFalse(TextUtils.isEmpty(Utility.dumpCursor(c3))); 337 assertFalse(TextUtils.isEmpty(Utility.dumpCursor(null))); 338 } 339 340 public void testCloseTraceCursorWrapper() { 341 final Cursor org = new MatrixCursorWithCachedColumns(new String[] {"col"}); 342 final Utility.CloseTraceCursorWrapper c = 343 Utility.CloseTraceCursorWrapper.alwaysCreateForTest(org); 344 345 // Not closed -- no stack trace 346 assertNull(Utility.CloseTraceCursorWrapper.getTraceIfAvailable(c)); 347 Utility.CloseTraceCursorWrapper.log(c); // shouldn't crash 348 349 // Close, now stack trace should be available 350 c.close(); 351 assertNotNull(Utility.CloseTraceCursorWrapper.getTraceIfAvailable(c)); 352 Utility.CloseTraceCursorWrapper.log(c); 353 354 // shouldn't crash 355 Utility.CloseTraceCursorWrapper.log(null); 356 } 357 358 public void brokentestAppendBold() { 359 SpannableStringBuilder ssb = new SpannableStringBuilder(); 360 ssb.append("no"); 361 362 assertEquals(ssb, Utility.appendBold(ssb, "BO")); 363 364 assertEquals("noBO", ssb.toString()); 365 // TODO check style -- but how? 366 } 367 368 public void testAreStringsEqual() { 369 String s1; 370 String s2; 371 372 s1 = new String("Foo"); 373 s2 = s1; 374 assertTrue(Utility.areStringsEqual(s1, s2)); 375 376 s2 = new String("Foo"); 377 assertTrue(Utility.areStringsEqual(s1, s2)); 378 379 s2 = "Bar"; 380 assertFalse(Utility.areStringsEqual(s1, s2)); 381 382 s2 = null; 383 assertFalse(Utility.areStringsEqual(s1, s2)); 384 385 s1 = null; 386 s2 = "Bar"; 387 assertFalse(Utility.areStringsEqual(s1, s2)); 388 389 s1 = null; 390 s2 = null; 391 assertTrue(Utility.areStringsEqual(s1, s2)); 392 } 393 394 public void testIsServerNameValid() { 395 assertTrue(Utility.isServerNameValid("a")); 396 assertTrue(Utility.isServerNameValid("gmail")); 397 assertTrue(Utility.isServerNameValid("gmail.com")); 398 assertTrue(Utility.isServerNameValid("gmail.com.x.y.z")); 399 assertTrue(Utility.isServerNameValid(" gmail.com.x.y.z ")); 400 401 assertFalse(Utility.isServerNameValid("")); 402 assertFalse(Utility.isServerNameValid("$")); 403 assertFalse(Utility.isServerNameValid(" ")); 404 } 405 406 private static Collection<Long> toColleciton(long... values) { 407 ArrayList<Long> ret = new ArrayList<Long>(); 408 for (long v : values) { 409 ret.add(v); 410 } 411 return ret; 412 } 413 414 public void brokentestBuildInSelection() { 415 assertEquals("", Utility.buildInSelection("c", null)); 416 assertEquals("", Utility.buildInSelection("c", toColleciton())); 417 assertEquals("c in (1)", Utility.buildInSelection("c", toColleciton(1))); 418 assertEquals("c in (1,2)", Utility.buildInSelection("c", toColleciton(1, 2))); 419 assertEquals("c in (1,2,-500)", Utility.buildInSelection("c", toColleciton(1, 2, -500))); 420 } 421 } 422