• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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.calendar;
18 
19 import com.android.calendar.Utils;
20 import com.android.calendar.CalendarUtils.TimeZoneUtils;
21 
22 import android.content.Context;
23 import android.content.res.Configuration;
24 import android.content.res.Resources;
25 import android.content.res.Resources.NotFoundException;
26 import android.database.MatrixCursor;
27 import android.provider.CalendarContract.CalendarCache;
28 import android.test.mock.MockResources;
29 import android.test.suitebuilder.annotation.SmallTest;
30 import android.test.suitebuilder.annotation.Smoke;
31 import android.text.Spannable;
32 import android.text.SpannableString;
33 import android.text.format.Time;
34 import android.text.style.URLSpan;
35 import android.util.DisplayMetrics;
36 import android.util.Log;
37 import android.view.Display;
38 import android.view.View;
39 import android.view.WindowManager;
40 import android.view.ViewGroup.LayoutParams;
41 import android.widget.TextView;
42 
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.HashMap;
46 import java.util.Iterator;
47 import java.util.Locale;
48 
49 import junit.framework.TestCase;
50 
51 /**
52  * Test class for verifying helper functions in Calendar's Utils
53  *
54  * You can run these tests with the following command:
55  * "adb shell am instrument -w -e class com.android.calendar.UtilsTests
56  *          com.android.calendar.tests/android.test.InstrumentationTestRunner"
57  */
58 public class UtilsTests extends TestCase {
59     HashMap<String, Boolean> mIsDuplicateName;
60     HashMap<String, Boolean> mIsDuplicateNameExpected;
61     MatrixCursor mDuplicateNameCursor;
62     private DbTestUtils dbUtils;
63     private final TimeZoneUtils timezoneUtils = new TimeZoneUtils(Utils.SHARED_PREFS_NAME);
64 
65     private static final int NAME_COLUMN = 0;
66     private static final String[] DUPLICATE_NAME_COLUMNS = new String[] { "name" };
67     private static final String[][] DUPLICATE_NAMES = new String[][] {
68         {"Pepper Pots"},
69         {"Green Goblin"},
70         {"Pepper Pots"},
71         {"Peter Parker"},
72         {"Silver Surfer"},
73         {"John Jameson"},
74         {"John Jameson"},
75         {"Pepper Pots"}
76     };
77     // First date is Thursday, Jan 1st, 1970.
78     private static final int[] JULIAN_DAYS = {2440588, 2440589, 2440590, 2440591, 2440592, 2440593,
79             2440594, 2440595, 2440596, 2440597, 2440598, 2440599, 2440600, 2440601
80     };
81     private static final int[] EXPECTED_WEEK_MONDAY_START = {
82             0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2 };
83     private static final int[] EXPECTED_WEEK_SUNDAY_START = {
84             0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2 };
85     private static final int[] EXPECTED_WEEK_SATURDAY_START = {
86             0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2 };
87     private static final int[] WEEKS_FOR_JULIAN_MONDAYS = {1, 2};
88     private static final int[] EXPECTED_JULIAN_MONDAYS = {2440592, 2440599};
89 
90     private static final int NOW_MONTH = 3; // April
91     private static final int NOW_DAY = 10;
92     private static final int NOW_YEAR = 2012;
93     private static final long NOW_TIME = createTimeInMillis(5, 5, 5, NOW_DAY, NOW_MONTH, NOW_YEAR);
94     private static final String DEFAULT_TIMEZONE = Time.getCurrentTimezone();
95 
96     /**
97      * Mock resources.  Add translation strings for test here.
98      */
99     private static class ResourcesForTest extends MockResources {
100         @Override
getString(int id)101         public String getString(int id) {
102             if (id == R.string.today) {
103                 return "Today";
104             }
105             if (id == R.string.tomorrow) {
106                 return "Tomorrow";
107             }
108             throw new IllegalArgumentException("unexpected resource ID: " + id);
109         }
110 
111         @Override
getString(int id, Object... formatArgs)112         public String getString(int id, Object... formatArgs) {
113             if (id == R.string.today_at_time_fmt) {
114                 return String.format("Today at %s", formatArgs);
115             }
116             if (id == R.string.tomorrow_at_time_fmt) {
117                 return String.format("Tomorrow at %s", formatArgs);
118             }
119             if (id == R.string.date_time_fmt) {
120                 return String.format("%s, %s", formatArgs);
121             }
122             throw new IllegalArgumentException("unexpected resource ID: " + id);
123         }
124 
125         @Override
getConfiguration()126         public Configuration getConfiguration() {
127             Configuration config = new Configuration();
128             config.locale = Locale.getDefault();
129             return config;
130         }
131 
132         @Override
getDisplayMetrics()133         public DisplayMetrics getDisplayMetrics(){
134             DisplayMetrics metrics = new DisplayMetrics();
135             metrics.density = 2.0f;
136             return metrics;
137         }
138     }
139 
createTimeInMillis(int second, int minute, int hour, int monthDay, int month, int year)140     private static long createTimeInMillis(int second, int minute, int hour, int monthDay,
141             int month, int year) {
142         return createTimeInMillis(second, minute, hour, monthDay, month, year,
143                 Time.getCurrentTimezone());
144     }
145 
createTimeInMillis(int second, int minute, int hour, int monthDay, int month, int year, String timezone)146     private static long createTimeInMillis(int second, int minute, int hour, int monthDay,
147             int month, int year, String timezone) {
148         Time t = new Time(timezone);
149         t.set(second, minute, hour, monthDay, month, year);
150         t.normalize(false);
151         return t.toMillis(false);
152     }
153 
setTimezone(String tz)154     private void setTimezone(String tz) {
155         timezoneUtils.setTimeZone(dbUtils.getContext(), tz);
156     }
157 
158     @Override
setUp()159     public void setUp() {
160         mIsDuplicateName = new HashMap<String, Boolean> ();
161         mDuplicateNameCursor = new MatrixCursor(DUPLICATE_NAME_COLUMNS);
162         for (int i = 0; i < DUPLICATE_NAMES.length; i++) {
163             mDuplicateNameCursor.addRow(DUPLICATE_NAMES[i]);
164         }
165 
166         mIsDuplicateNameExpected = new HashMap<String, Boolean> ();
167         mIsDuplicateNameExpected.put("Pepper Pots", true);
168         mIsDuplicateNameExpected.put("Green Goblin", false);
169         mIsDuplicateNameExpected.put("Peter Parker", false);
170         mIsDuplicateNameExpected.put("Silver Surfer", false);
171         mIsDuplicateNameExpected.put("John Jameson", true);
172 
173         // Set up fake db.
174         dbUtils = new DbTestUtils(new ResourcesForTest());
175         dbUtils.getContentResolver().addProvider("settings", dbUtils.getContentProvider());
176         dbUtils.getContentResolver().addProvider(CalendarCache.URI.getAuthority(),
177                 dbUtils.getContentProvider());
178 
179         setTimezone(DEFAULT_TIMEZONE);
180     }
181 
182     @Override
tearDown()183     public void tearDown() {
184         mDuplicateNameCursor.close();
185 
186         // Must reset the timezone here, because even though the fake provider will be
187         // recreated/cleared, TimeZoneUtils statically holds on to a cached value.
188         setTimezone(Time.getCurrentTimezone());
189     }
190 
191     @Smoke
192     @SmallTest
testCheckForDuplicateNames()193     public void testCheckForDuplicateNames() {
194         Utils.checkForDuplicateNames(mIsDuplicateName, mDuplicateNameCursor, NAME_COLUMN);
195         assertEquals(mIsDuplicateNameExpected, mIsDuplicateName);
196     }
197 
198     @Smoke
199     @SmallTest
testGetWeeksSinceEpochFromJulianDay()200     public void testGetWeeksSinceEpochFromJulianDay() {
201         for (int i = 0; i < JULIAN_DAYS.length; i++) {
202             assertEquals(EXPECTED_WEEK_MONDAY_START[i],
203                     Utils.getWeeksSinceEpochFromJulianDay(JULIAN_DAYS[i], Time.MONDAY));
204             assertEquals(EXPECTED_WEEK_SUNDAY_START[i],
205                     Utils.getWeeksSinceEpochFromJulianDay(JULIAN_DAYS[i], Time.SUNDAY));
206             assertEquals(EXPECTED_WEEK_SATURDAY_START[i],
207                     Utils.getWeeksSinceEpochFromJulianDay(JULIAN_DAYS[i], Time.SATURDAY));
208         }
209     }
210 
211     @Smoke
212     @SmallTest
testGetJulianMondayFromWeeksSinceEpoch()213     public void testGetJulianMondayFromWeeksSinceEpoch() {
214         for (int i = 0; i < WEEKS_FOR_JULIAN_MONDAYS.length; i++) {
215             assertEquals(EXPECTED_JULIAN_MONDAYS[i],
216                     Utils.getJulianMondayFromWeeksSinceEpoch(WEEKS_FOR_JULIAN_MONDAYS[i]));
217         }
218     }
219 
220     @Smoke
221     @SmallTest
testEquals()222     public void testEquals() {
223         assertTrue(Utils.equals(null, null));
224         assertFalse(Utils.equals("", null));
225         assertFalse(Utils.equals(null, ""));
226         assertTrue(Utils.equals("",""));
227 
228         Integer int1 = new Integer(1);
229         Integer int2 = new Integer(1);
230         assertTrue(Utils.equals(int1, int2));
231     }
232 
233 
234     // Helper function to create test events for BusyBits testing
buildTestEvent(int startTime, int endTime, boolean allDay, int startDay, int endDay)235     Event buildTestEvent(int startTime, int endTime, boolean allDay, int startDay, int endDay) {
236         Event e = new Event();
237         e.startTime = startTime;
238         e.endTime = endTime;
239         e.allDay = allDay;
240         e.startDay = startDay;
241         e.endDay = endDay;
242         e.startMillis = e.startDay * 1000L * 3600L * 24L + e.startTime * 60L * 1000L;
243         e.endMillis = e.endDay * 1000L * 3600L * 24L + e.endTime * 60L * 1000L;
244         return e;
245     }
246 
247     @Smoke
248     @SmallTest
testCreateBusyBitSegments()249     public void testCreateBusyBitSegments() {
250 
251   /*      ArrayList<Event> events = new ArrayList<Event>();
252 
253         // Test cases that should return null
254         // Empty events list
255         assertEquals(null, Utils.createBusyBitSegments(10, 30, 100, 200, 0, events));
256         // No events list
257         assertEquals(null, Utils.createBusyBitSegments(10, 30, 100, 200, 0, null));
258 
259         events.add(buildTestEvent(100, 130, false, 1, 1));
260         events.add(buildTestEvent(1000, 1030, false, 1, 1));
261         // Illegal pixel positions
262         assertEquals(null, Utils.createBusyBitSegments(30, 10, 100, 200, 1, events));
263         // Illegal start and end times
264         assertEquals(null, Utils.createBusyBitSegments(10, 30, 200, 100, 1, events));
265         assertEquals(null, Utils.createBusyBitSegments(10, 30, -10, 100, 1, events));
266         assertEquals(null, Utils.createBusyBitSegments(10, 30, 24 * 60 + 100, 24 * 60 + 200, 1,
267                 events));
268         assertEquals(null, Utils.createBusyBitSegments(10, 30, 200, 24 * 60 + 100, 1, events));
269         assertEquals(null, Utils.createBusyBitSegments(10, 30, 200, -100, 1, events));
270         // No Events in time frame
271         assertEquals(null, Utils.createBusyBitSegments(10, 30, 500, 900, 1, events));
272 
273         // Test event that spans over the day
274         events.clear();
275         events.add(buildTestEvent(100, 300, false, 1, 5));
276         ArrayList<BusyBitsSegment> segments = new ArrayList<BusyBitsSegment>();
277         assertEquals(null, Utils.createBusyBitSegments(0, 250, 200, 1200, 3, events));
278 
279         // test zero times events, events that are partially in the time span
280         // and all day events
281         events.clear();
282         events.add(buildTestEvent(100, 300, false, 1, 1));
283         events.add(buildTestEvent(1100, 1300, false, 1, 1));
284         events.add(buildTestEvent(500, 600, true, 1, 1));
285         events.add(buildTestEvent(700, 700, false, 1, 1));
286         segments.clear();
287         segments.add(new BusyBitsSegment(0, 10, false));
288         segments.add(new BusyBitsSegment(90, 100, false));
289         assertEquals(segments, Utils.createBusyBitSegments(0, 100, 200, 1200, 1, events));
290 
291         // Test event that spans over 2 days but start and end time do not
292         // overlap fully with tested time span
293 
294         events.clear();
295         events.add(buildTestEvent(23 * 60, 120, false, 1, 2));
296         segments.clear();
297         segments.add(new BusyBitsSegment(0, 120, false));
298         assertEquals(segments, Utils.createBusyBitSegments(0, 240, 60, 180, 2, events));
299 
300         // Test overlapped events (two draw sizes)
301         events.clear();
302         events.add(buildTestEvent(10, 200, false, 1, 1));
303         events.add(buildTestEvent(150, 250, false, 1, 1));
304         events.add(buildTestEvent(150, 250, false, 1, 1));
305         events.add(buildTestEvent(200, 400, false, 1, 1));
306         events.add(buildTestEvent(500, 700, false, 1, 1));
307         events.add(buildTestEvent(550, 600, false, 1, 1));
308         events.add(buildTestEvent(550, 580, false, 1, 1));
309         events.add(buildTestEvent(560, 570, false, 1, 1));
310         events.add(buildTestEvent(600, 700, false, 1, 1));
311         events.add(buildTestEvent(620, 700, false, 1, 1));
312         events.add(buildTestEvent(650, 700, false, 1, 1));
313         events.add(buildTestEvent(800, 900, false, 1, 1));
314         events.add(buildTestEvent(800, 900, false, 1, 1));
315         events.add(buildTestEvent(800, 850, false, 1, 1));
316         events.add(buildTestEvent(1000, 1200, false, 1, 1));
317         events.add(buildTestEvent(1000, 1200, false, 1, 1));
318         segments.clear();
319         segments.add(new BusyBitsSegment(100, 149, false));
320         segments.add(new BusyBitsSegment(150, 250, true));
321         segments.add(new BusyBitsSegment(251, 400, false));
322         segments.add(new BusyBitsSegment(500, 549, false));
323         segments.add(new BusyBitsSegment(550, 700, true));
324         segments.add(new BusyBitsSegment(800, 900, true));
325         segments.add(new BusyBitsSegment(1000, 1100, true));
326         assertEquals(segments, Utils.createBusyBitSegments(100, 1100, 100, 1100, 1, events));
327         segments.clear();
328         segments.add(new BusyBitsSegment(100, 111, false));
329         segments.add(new BusyBitsSegment(112, 137, true));
330         segments.add(new BusyBitsSegment(138, 175, false));
331         segments.add(new BusyBitsSegment(200, 211, false));
332         segments.add(new BusyBitsSegment(212, 250, true));
333         segments.add(new BusyBitsSegment(275, 300, true));
334         segments.add(new BusyBitsSegment(325, 350, true));
335         assertEquals(segments, Utils.createBusyBitSegments(100, 350, 100, 1100, 1, events));
336 */
337     }
338 
339     /**
340      * Tests the findNanpPhoneNumbers function.
341      */
342     @SmallTest
testFindNanpPhoneNumber()343     public void testFindNanpPhoneNumber() {
344         final String[] NO_NUMBERS = new String[] {};
345 
346         findPhoneNumber("", NO_NUMBERS);
347         findPhoneNumber("               ", NO_NUMBERS);
348         findPhoneNumber("123", NO_NUMBERS);
349         findPhoneNumber("how much wood", NO_NUMBERS);
350         findPhoneNumber("abc1-650-555-1212", NO_NUMBERS);
351         findPhoneNumber("abc 5551212 def", new String[] { "5551212" });
352         findPhoneNumber("1234567", NO_NUMBERS);
353         findPhoneNumber(" 2345678 ", new String[] { "2345678" });
354         findPhoneNumber("1234567890", NO_NUMBERS);
355         findPhoneNumber("12345678901", new String[] { "12345678901" });
356         findPhoneNumber("123456789012", NO_NUMBERS);
357         findPhoneNumber("+1-555-1212", NO_NUMBERS);
358         findPhoneNumber("+1 (650) 555-1212", new String[] { "+1 (650) 555-1212" });
359         findPhoneNumber("(650) 555-1212, (650) 555-1213",
360                 new String[] { "(650) 555-1212", "(650) 555-1213" });
361         findPhoneNumber("Call 555-1212, 555-1213 and also 555-1214.",
362                 new String[] { "555-1212", "555-1213", "555-1214." });
363         findPhoneNumber("555-1212,555-1213,555-1214", new String[] { "555-1212" });
364         findPhoneNumber("123 (650) 555-1212", new String[] { "(650) 555-1212" });
365         findPhoneNumber("1-650-555-1212", new String[] { "1-650-555-1212" });
366         findPhoneNumber("1650-555-1212", new String[] { "1650-555-1212" });
367         findPhoneNumber("1650 555-1212", new String[] { "1650 555-1212" });
368         findPhoneNumber("1650/555-1212", NO_NUMBERS);
369         findPhoneNumber("1650-555 1212", NO_NUMBERS);
370         findPhoneNumber("8-650-555-1212", NO_NUMBERS);
371         findPhoneNumber("8 650-555-1212", new String[] { "650-555-1212" });
372         findPhoneNumber("650.555.1212", new String[] { "650.555.1212" });
373         findPhoneNumber(" *#650.555.1212#*!", new String[] { "*#650.555.1212#*" });
374         findPhoneNumber("555.1212", new String[] { "555.1212" });
375         findPhoneNumber("6505551212 x123, 555-1212", new String[] { "6505551212", "555-1212" });
376         findPhoneNumber("6505551212x123", new String[] { "6505551212" });
377         findPhoneNumber("http://example.com/6505551212/", NO_NUMBERS);
378         findPhoneNumber("Mountain View, CA 94043 (650) 555-1212", new String[]{ "(650) 555-1212" });
379         findPhoneNumber("New York, NY 10001-0001", NO_NUMBERS);
380     }
381 
382     /**
383      * Finds the numbers in a block of text, and checks to see if the positions of the numbers
384      * match the expected values.
385      *
386      * @param text The text to search.
387      * @param matches Pairs of start/end positions.
388      */
findPhoneNumber(String text, String[] matches)389     private static void findPhoneNumber(String text, String[] matches) {
390         int[] results = Utils.findNanpPhoneNumbers(text);
391 
392         assertEquals(0, results.length % 2);
393 
394         if (results.length / 2 != matches.length) {
395             fail("Text '" + text + "': expected " + matches.length
396                     + " matches, found " + results.length / 2);
397         }
398 
399         for (int i = 0; i < results.length / 2; i++) {
400             CharSequence seq = text.subSequence(results[i*2], results[i*2 + 1]);
401             assertEquals(matches[i], seq);
402         }
403     }
404 
405     /**
406      * Tests the linkify section of event locations.
407      */
408     @SmallTest
testExtendedLinkify()409     public void testExtendedLinkify() {
410         final URLSpan[] NO_LINKS = new URLSpan[] {};
411         URLSpan span_tel01 = new URLSpan("tel:6505551234");
412         URLSpan span_tel02 = new URLSpan("tel:5555678");
413         URLSpan span_tel03 = new URLSpan("tel:+16505551234");
414         URLSpan span_tel04 = new URLSpan("tel:16505551234");
415         URLSpan span_web = new URLSpan("http://www.google.com");
416         URLSpan span_geo01 =
417                 new URLSpan("geo:0,0?q=1600+Amphitheatre+Parkway%2C+Mountain+View+CA+94043");
418         URLSpan span_geo02 =
419                 new URLSpan("geo:0,0?q=37.422081°, -122.084576°");
420         URLSpan span_geo03 =
421                 new URLSpan("geo:0,0?q=37.422081,-122.084576");
422         URLSpan span_geo04 =
423                 new URLSpan("geo:0,0?q=+37°25'19.49\", -122°5'4.47\"");
424         URLSpan span_geo05 =
425                 new URLSpan("geo:0,0?q=37°25'19.49\"N, 122°5'4.47\"W");
426         URLSpan span_geo06 =
427                 new URLSpan("geo:0,0?q=N 37° 25' 19.49\",  W 122° 5' 4.47\"");
428         URLSpan span_geo07 = new URLSpan("geo:0,0?q=non-specified address");
429 
430 
431         // First test without the last-ditch geo attempt.
432         // Phone spans.
433         findLinks("", NO_LINKS, false);
434         findLinks("(650) 555-1234", new URLSpan[]{span_tel01}, false);
435         findLinks("94043", NO_LINKS, false);
436         findLinks("123456789012", NO_LINKS, false);
437         findLinks("+1 (650) 555-1234", new URLSpan[]{span_tel03}, false);
438         findLinks("(650) 555 1234", new URLSpan[]{span_tel01}, false);
439         findLinks("1-650-555-1234", new URLSpan[]{span_tel04}, false);
440         findLinks("*#650.555.1234#*!", new URLSpan[]{span_tel01}, false);
441         findLinks("555.5678", new URLSpan[]{span_tel02}, false);
442 
443         // Web spans.
444         findLinks("http://www.google.com", new URLSpan[]{span_web}, false);
445 
446         // Geo spans.
447         findLinks("1600 Amphitheatre Parkway, Mountain View CA 94043",
448                 new URLSpan[]{span_geo01}, false);
449         findLinks("37.422081°, -122.084576°", new URLSpan[]{span_geo02}, false);
450         findLinks("37.422081,-122.084576", new URLSpan[]{span_geo03}, false);
451         findLinks("+37°25'19.49\", -122°5'4.47\"", new URLSpan[]{span_geo04}, false);
452         findLinks("37°25'19.49\"N, 122°5'4.47\"W", new URLSpan[]{span_geo05}, false);
453         findLinks("N 37° 25' 19.49\",  W 122° 5' 4.47\"", new URLSpan[]{span_geo06}, false);
454 
455         // Multiple spans.
456         findLinks("(650) 555-1234 1600 Amphitheatre Parkway, Mountain View CA 94043",
457                 new URLSpan[]{span_tel01, span_geo01}, false);
458         findLinks("(650) 555-1234, 555-5678", new URLSpan[]{span_tel01, span_tel02}, false);
459 
460 
461         // Now test using the last-ditch geo attempt.
462         findLinks("", NO_LINKS, true);
463         findLinks("(650) 555-1234", new URLSpan[]{span_tel01}, true);
464         findLinks("http://www.google.com", new URLSpan[]{span_web}, true);
465         findLinks("1600 Amphitheatre Parkway, Mountain View CA 94043",
466                 new URLSpan[]{span_geo01}, true);
467         findLinks("37.422081°, -122.084576°", new URLSpan[]{span_geo02}, true);
468         findLinks("37.422081,-122.084576", new URLSpan[]{span_geo03}, true);
469         findLinks("+37°25'19.49\", -122°5'4.47\"", new URLSpan[]{span_geo04}, true);
470         findLinks("37°25'19.49\"N, 122°5'4.47\"W", new URLSpan[]{span_geo05}, true);
471         findLinks("N 37° 25' 19.49\",  W 122° 5' 4.47\"", new URLSpan[]{span_geo06}, true);
472         findLinks("non-specified address", new URLSpan[]{span_geo07}, true);
473     }
474 
findLinks(String text, URLSpan[] matches, boolean lastDitchGeo)475     private static void findLinks(String text, URLSpan[] matches, boolean lastDitchGeo) {
476         Spannable spanText = Utils.extendedLinkify(text, lastDitchGeo);
477         URLSpan[] spansFound = spanText.getSpans(0, spanText.length(), URLSpan.class);
478         assertEquals(matches.length, spansFound.length);
479 
480         // Make sure the expected matches list of URLs is the same as that returned by linkify.
481         ArrayList<URLSpan> matchesArrayList = new ArrayList<URLSpan>(Arrays.asList(matches));
482         for (URLSpan spanFound : spansFound) {
483             Iterator<URLSpan> matchesIt = matchesArrayList.iterator();
484             boolean removed = false;
485             while (matchesIt.hasNext()) {
486                 URLSpan match = matchesIt.next();
487                 if (match.getURL().equals(spanFound.getURL())) {
488                     matchesIt.remove();
489                     removed = true;
490                     break;
491                 }
492             }
493             if (!removed) {
494                 // If a match was not found for the current spanFound, the lists aren't equal.
495                 fail("No match found for span: "+spanFound.getURL());
496             }
497         }
498 
499         // As a sanity check, ensure the matches list is empty, as each item should have been
500         // removed by going through the spans returned by linkify.
501         assertTrue(matchesArrayList.isEmpty());
502     }
503 
504     @SmallTest
testGetDisplayedDatetime_differentYear()505     public void testGetDisplayedDatetime_differentYear() {
506         // 4/12/2000 5pm - 4/12/2000 6pm
507         long start = createTimeInMillis(0, 0, 17, 12, 3, 2000);
508         long end = createTimeInMillis(0, 0, 18, 12, 3, 2000);
509         String result = Utils.getDisplayedDatetime(start, end, NOW_TIME, DEFAULT_TIMEZONE,
510                 false, dbUtils.getContext());
511         assertEquals("Wednesday, April 12, 2000, 5:00PM \u2013 6:00PM", result);
512 
513         // 12/31/2012 5pm - 1/1/2013 6pm
514         start = createTimeInMillis(0, 0, 17, 31, 11, 2012);
515         end = createTimeInMillis(0, 0, 18, 1, 0, 2013);
516         result = Utils.getDisplayedDatetime(start, end, NOW_TIME, DEFAULT_TIMEZONE,
517                 false, dbUtils.getContext());
518         assertEquals("Mon, Dec 31, 2012, 5:00PM – Tue, Jan 1, 2013, 6:00PM", result);
519     }
520 
521     @SmallTest
testGetDisplayedDatetime_sameYear()522     public void testGetDisplayedDatetime_sameYear() {
523         // 4/12/2012 5pm - 4/12/2012 6pm
524         long start = createTimeInMillis(0, 0, 17, 12, 3, 2012);
525         long end = createTimeInMillis(0, 0, 18, 12, 3, 2012);
526         String result = Utils.getDisplayedDatetime(start, end, NOW_TIME, DEFAULT_TIMEZONE,
527                 false, dbUtils.getContext());
528         assertEquals("Thursday, April 12, 2012, 5:00PM \u2013 6:00PM", result);
529     }
530 
531     @SmallTest
testGetDisplayedDatetime_today()532     public void testGetDisplayedDatetime_today() {
533         // 4/10/2012 5pm - 4/10/2012 6pm
534         long start = createTimeInMillis(0, 0, 17, NOW_DAY, NOW_MONTH, NOW_YEAR);
535         long end = createTimeInMillis(0, 0, 18, NOW_DAY, NOW_MONTH, NOW_YEAR);
536         String result = Utils.getDisplayedDatetime(start, end, NOW_TIME, DEFAULT_TIMEZONE,
537                 false, dbUtils.getContext());
538         assertEquals("Today at 5:00PM \u2013 6:00PM", result);
539     }
540 
541     @SmallTest
testGetDisplayedDatetime_todayMidnight()542     public void testGetDisplayedDatetime_todayMidnight() {
543         // 4/10/2012 5pm - 4/11/2012 12am
544         long start = createTimeInMillis(0, 0, 17, NOW_DAY, NOW_MONTH, NOW_YEAR);
545         long end = createTimeInMillis(0, 0, 0, NOW_DAY + 1, NOW_MONTH, NOW_YEAR);
546         String result = Utils.getDisplayedDatetime(start, end, NOW_TIME, DEFAULT_TIMEZONE,
547                 false, dbUtils.getContext());
548         assertEquals("Today at 5:00PM \u2013 midnight", result);
549     }
550 
551     @SmallTest
testGetDisplayedDatetime_tomorrow()552     public void testGetDisplayedDatetime_tomorrow() {
553         // 4/11/2012 12:01AM - 4/11/2012 11:59pm
554         long start = createTimeInMillis(0, 1, 0, NOW_DAY + 1, NOW_MONTH, NOW_YEAR);
555         long end = createTimeInMillis(0, 59, 23, NOW_DAY + 1, NOW_MONTH, NOW_YEAR);
556         String result = Utils.getDisplayedDatetime(start, end, NOW_TIME, DEFAULT_TIMEZONE,
557                 false, dbUtils.getContext());
558         assertEquals("Tomorrow at 12:01AM \u2013 11:59PM", result);
559     }
560 
561     @SmallTest
testGetDisplayedDatetime_yesterday()562     public void testGetDisplayedDatetime_yesterday() {
563         // 4/9/2012 5pm - 4/9/2012 6pm
564         long start = createTimeInMillis(0, 0, 17, 9, 3, 2012);
565         long end = createTimeInMillis(0, 0, 18, 9, 3, 2012);
566         String result = Utils.getDisplayedDatetime(start, end, NOW_TIME, DEFAULT_TIMEZONE,
567                 false, dbUtils.getContext());
568         assertEquals("Monday, April 9, 2012, 5:00PM \u2013 6:00PM", result);
569     }
570 
571     @SmallTest
testGetDisplayedDatetime_multiDay()572     public void testGetDisplayedDatetime_multiDay() {
573         // 4/10/2012 12:01AM - 4/11/2012 12:01AM
574         long start = createTimeInMillis(0, 1, 0, NOW_DAY, NOW_MONTH, NOW_YEAR);
575         long end = createTimeInMillis(0, 1, 0, NOW_DAY + 1, NOW_MONTH, NOW_YEAR);
576         String result = Utils.getDisplayedDatetime(start, end, NOW_TIME, DEFAULT_TIMEZONE,
577                 false, dbUtils.getContext());
578         assertEquals("Tue, Apr 10, 2012, 12:01AM \u2013 Wed, Apr 11, 2012, 12:01AM", result);
579     }
580 
581     @SmallTest
testGetDisplayedDatetime_allDay()582     public void testGetDisplayedDatetime_allDay() {
583         // 4/2/2012 12:00AM - 4/3/2012 12:00AM
584         long start = createTimeInMillis(0, 0, 0, 2, 3, NOW_YEAR, Time.TIMEZONE_UTC);
585         long end = createTimeInMillis(0, 0, 0, 3, 3, NOW_YEAR, Time.TIMEZONE_UTC);
586         String result = Utils.getDisplayedDatetime(start, end, NOW_TIME, DEFAULT_TIMEZONE,
587                 true, dbUtils.getContext());
588         assertEquals("Monday, April 2, 2012", result);
589     }
590 
591     @SmallTest
testGetDisplayedDatetime_allDayToday()592     public void testGetDisplayedDatetime_allDayToday() {
593         // 4/10/2012 12:00AM - 4/11/2012 12:00AM
594         long start = createTimeInMillis(0, 0, 0, NOW_DAY, NOW_MONTH, NOW_YEAR, Time.TIMEZONE_UTC);
595         long end = createTimeInMillis(0, 0, 0, NOW_DAY + 1, NOW_MONTH, NOW_YEAR, Time.TIMEZONE_UTC);
596         String result = Utils.getDisplayedDatetime(start, end, NOW_TIME, DEFAULT_TIMEZONE,
597                 true, dbUtils.getContext());
598         assertEquals("Today", result);
599     }
600 
601     @SmallTest
testGetDisplayedDatetime_allDayMultiday()602     public void testGetDisplayedDatetime_allDayMultiday() {
603         // 4/10/2012 12:00AM - 4/13/2012 12:00AM
604         long start = createTimeInMillis(0, 0, 0, NOW_DAY, NOW_MONTH, NOW_YEAR, Time.TIMEZONE_UTC);
605         long end = createTimeInMillis(0, 0, 0, NOW_DAY + 3, NOW_MONTH, NOW_YEAR, Time.TIMEZONE_UTC);
606         String result = Utils.getDisplayedDatetime(start, end, NOW_TIME, DEFAULT_TIMEZONE,
607                 true, dbUtils.getContext());
608         assertEquals("Tuesday, April 10, 2012 \u2013 Thursday, April 12, 2012", result);
609     }
610 
611     @SmallTest
testGetDisplayedDatetime_differentTimezone()612     public void testGetDisplayedDatetime_differentTimezone() {
613         String localTz = "America/New_York";
614         String eventTz = "America/Los_Angeles";
615         setTimezone(localTz);
616 
617         // 4/12/2012 5pm - 4/12/2012 6pm (Pacific)
618         long start = createTimeInMillis(0, 0, 17, 12, 3, 2012, eventTz);
619         long end = createTimeInMillis(0, 0, 18, 12, 3, 2012, eventTz);
620         String result = Utils.getDisplayedDatetime(start, end, NOW_TIME, localTz, false,
621                 dbUtils.getContext());
622         assertEquals("Thursday, April 12, 2012, 8:00PM \u2013 9:00PM", result);
623     }
624 
625     @SmallTest
testGetDisplayedDatetime_allDayDiffTimezone()626     public void testGetDisplayedDatetime_allDayDiffTimezone() {
627         String localTz = "America/New_York";
628         setTimezone(localTz);
629 
630         // 4/2/2012 12:00AM - 4/3/2012 12:00AM
631         long start = createTimeInMillis(0, 0, 0, 2, 3, NOW_YEAR, Time.TIMEZONE_UTC);
632         long end = createTimeInMillis(0, 0, 0, 3, 3, NOW_YEAR, Time.TIMEZONE_UTC);
633         String result = Utils.getDisplayedDatetime(start, end, NOW_TIME, localTz, true,
634                 dbUtils.getContext());
635         assertEquals("Monday, April 2, 2012", result);
636     }
637 
638     @SmallTest
testGetDisplayedDatetime_allDayTomorrowDiffTimezone()639     public void testGetDisplayedDatetime_allDayTomorrowDiffTimezone() {
640         String localTz = "America/New_York";
641         setTimezone(localTz);
642 
643         // 4/2/2012 12:00AM - 4/3/2012 12:00AM
644         long start = createTimeInMillis(0, 0, 0, NOW_DAY + 1, NOW_MONTH, NOW_YEAR,
645                 Time.TIMEZONE_UTC);
646         long end = createTimeInMillis(0, 0, 0, NOW_DAY + 2, NOW_MONTH, NOW_YEAR,
647                 Time.TIMEZONE_UTC);
648         String result = Utils.getDisplayedDatetime(start, end, NOW_TIME, localTz, true,
649                 dbUtils.getContext());
650         assertEquals("Tomorrow", result);
651     }
652 
653     // TODO: add tests for army time.
654 
655     @SmallTest
testGetDisplayedTimezone_sameTimezone()656     public void testGetDisplayedTimezone_sameTimezone() {
657         String localTz = "America/New_York";
658         setTimezone(localTz);
659 
660         // 4/12/2012 5pm
661         long start = createTimeInMillis(0, 0, 17, 12, 3, 2012, localTz);
662         assertNull(Utils.getDisplayedTimezone(start, localTz, localTz));
663     }
664 
665     @SmallTest
testGetDisplayedTimezone_differentTimezone()666     public void testGetDisplayedTimezone_differentTimezone() {
667         String localTz = "America/New_York";
668         String eventTz = "America/Los_Angeles";
669         setTimezone(localTz);
670 
671         // 1/12/2012 5pm (not daylight savings)
672         long start = createTimeInMillis(0, 0, 17, 12, 0, 2012, eventTz);
673         assertEquals("EST", Utils.getDisplayedTimezone(start, localTz, eventTz));
674     }
675 
676     @SmallTest
testGetDisplayedTimezone_differentTimezoneDst()677     public void testGetDisplayedTimezone_differentTimezoneDst() {
678         String localTz = "America/New_York";
679         String eventTz = "America/Los_Angeles";
680         setTimezone(localTz);
681 
682         // 4/12/2012 5pm (daylight savings)
683         long start = createTimeInMillis(0, 0, 17, 12, 3, 2012, eventTz);
684         assertEquals("EDT", Utils.getDisplayedTimezone(start, localTz, eventTz));
685     }
686 }
687