1/* 2 * Copyright (C) 2015 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 17package com.android.tv.recommendation; 18 19import android.test.MoreAsserts; 20import android.test.suitebuilder.annotation.SmallTest; 21 22import com.android.tv.data.Program; 23import com.android.tv.recommendation.RoutineWatchEvaluator.ProgramTime; 24 25import java.util.Arrays; 26import java.util.Calendar; 27import java.util.List; 28import java.util.TreeSet; 29import java.util.concurrent.TimeUnit; 30 31@SmallTest 32public class RoutineWatchEvaluatorTest extends EvaluatorTestCase<RoutineWatchEvaluator> { 33 private static class ScoredItem implements Comparable<ScoredItem> { 34 private final String mBase; 35 private final String mText; 36 private final double mScore; 37 38 private ScoredItem(String base, String text) { 39 this.mBase = base; 40 this.mText = text; 41 this.mScore = RoutineWatchEvaluator.calculateTitleMatchScore(base, text); 42 } 43 44 @Override 45 public int compareTo(ScoredItem scoredItem) { 46 return Double.compare(mScore, scoredItem.mScore); 47 } 48 49 @Override 50 public String toString() { 51 return mBase + " scored with " + mText + " is " + mScore; 52 } 53 } 54 55 private static ScoredItem score(String t1, String t2) { 56 return new ScoredItem(t1, t2); 57 } 58 59 @Override 60 public RoutineWatchEvaluator createEvaluator() { 61 return new RoutineWatchEvaluator(); 62 } 63 64 public void testSplitTextToWords() { 65 assertSplitTextToWords(""); 66 assertSplitTextToWords("Google", "Google"); 67 assertSplitTextToWords("The Big Bang Theory", "The", "Big", "Bang", "Theory"); 68 assertSplitTextToWords("Hello, world!", "Hello", "world"); 69 assertSplitTextToWords("Adam's Rib", "Adam's", "Rib"); 70 assertSplitTextToWords("G.I. Joe", "G.I", "Joe"); 71 assertSplitTextToWords("A.I.", "A.I"); 72 } 73 74 public void testCalculateMaximumMatchedWordSequenceLength() { 75 assertMaximumMatchedWordSequenceLength(0, "", "Google"); 76 assertMaximumMatchedWordSequenceLength(2, "The Big Bang Theory", "Big Bang"); 77 assertMaximumMatchedWordSequenceLength(2, "The Big Bang Theory", "Theory Of Big Bang"); 78 assertMaximumMatchedWordSequenceLength(4, "The Big Bang Theory", "The Big Bang Theory"); 79 assertMaximumMatchedWordSequenceLength(1, "Modern Family", "Family Guy"); 80 assertMaximumMatchedWordSequenceLength(1, "The Simpsons", "The Walking Dead"); 81 assertMaximumMatchedWordSequenceLength(3, "Game Of Thrones 1", "Game Of Thrones 6"); 82 assertMaximumMatchedWordSequenceLength(0, "Dexter", "Friends"); 83 } 84 85 public void testCalculateTitleMatchScore_empty() { 86 assertEquals(0.0, RoutineWatchEvaluator.calculateTitleMatchScore("", "")); 87 assertEquals(0.0, RoutineWatchEvaluator.calculateTitleMatchScore("foo", "")); 88 assertEquals(0.0, RoutineWatchEvaluator.calculateTitleMatchScore("", "foo")); 89 } 90 91 public void testCalculateTitleMatchScore_spaces() { 92 assertEquals(0.0, RoutineWatchEvaluator.calculateTitleMatchScore(" ", " ")); 93 assertEquals(0.0, RoutineWatchEvaluator.calculateTitleMatchScore("foo", " ")); 94 assertEquals(0.0, RoutineWatchEvaluator.calculateTitleMatchScore(" ", "foo")); 95 } 96 97 98 public void testCalculateTitleMatchScore_null() { 99 assertEquals(0.0, RoutineWatchEvaluator.calculateTitleMatchScore(null, null)); 100 assertEquals(0.0, RoutineWatchEvaluator.calculateTitleMatchScore("foo", null)); 101 assertEquals(0.0, RoutineWatchEvaluator.calculateTitleMatchScore(null, "foo")); 102 } 103 104 public void testCalculateTitleMatchScore_longerMatchIsBetter() { 105 String base = "foo bar baz"; 106 assertInOrder( 107 score(base, ""), 108 score(base, "bar"), 109 score(base, "foo bar"), 110 score(base, "foo bar baz")); 111 } 112 113 public void testProgramTime_createFromProgram() { 114 Calendar time = Calendar.getInstance(); 115 int todayDayOfWeek = time.get(Calendar.DAY_OF_WEEK); 116 // Value of DayOfWeek is between 1 and 7 (inclusive). 117 int tomorrowDayOfWeek = (todayDayOfWeek % 7) + 1; 118 119 // Today 00:00 - 01:00. 120 ProgramTime programTimeToday0000_0100 = ProgramTime.createFromProgram( 121 createDummyProgram(todayAtHourMin(0, 0), TimeUnit.HOURS.toMillis(1))); 122 assertProgramTime(todayDayOfWeek, hourMinuteToSec(0, 0), hourMinuteToSec(1, 0), 123 programTimeToday0000_0100); 124 125 // Today 23:30 - 24:30. 126 ProgramTime programTimeToday2330_2430 = ProgramTime.createFromProgram( 127 createDummyProgram(todayAtHourMin(23, 30), TimeUnit.HOURS.toMillis(1))); 128 assertProgramTime(todayDayOfWeek, hourMinuteToSec(23, 30), hourMinuteToSec(24, 30), 129 programTimeToday2330_2430); 130 131 // Tomorrow 00:00 - 01:00. 132 ProgramTime programTimeTomorrow0000_0100 = ProgramTime.createFromProgram( 133 createDummyProgram(tomorrowAtHourMin(0, 0), TimeUnit.HOURS.toMillis(1))); 134 assertProgramTime(tomorrowDayOfWeek, hourMinuteToSec(0, 0), hourMinuteToSec(1, 0), 135 programTimeTomorrow0000_0100); 136 137 // Tomorrow 23:30 - 24:30. 138 ProgramTime programTimeTomorrow2330_2430 = ProgramTime.createFromProgram( 139 createDummyProgram(tomorrowAtHourMin(23, 30), TimeUnit.HOURS.toMillis(1))); 140 assertProgramTime(tomorrowDayOfWeek, hourMinuteToSec(23, 30), hourMinuteToSec(24, 30), 141 programTimeTomorrow2330_2430); 142 143 // Today 18:00 - Tomorrow 12:00. 144 ProgramTime programTimeToday1800_3600 = ProgramTime.createFromProgram( 145 createDummyProgram(todayAtHourMin(18, 0), TimeUnit.HOURS.toMillis(18))); 146 // Maximum duration of ProgramTime is 12 hours. 147 // So, this program looks like it ends at Tomorrow 06:00 (30:00). 148 assertProgramTime(todayDayOfWeek, hourMinuteToSec(18, 0), hourMinuteToSec(30, 0), 149 programTimeToday1800_3600); 150 } 151 152 public void testCalculateOverlappedIntervalScore() { 153 // Today 21:00 - 24:00. 154 ProgramTime programTimeToday2100_2400 = ProgramTime.createFromProgram( 155 createDummyProgram(todayAtHourMin(21, 0), TimeUnit.HOURS.toMillis(3))); 156 // Today 22:00 - 01:00. 157 ProgramTime programTimeToday2200_0100 = ProgramTime.createFromProgram( 158 createDummyProgram(todayAtHourMin(22, 0), TimeUnit.HOURS.toMillis(3))); 159 // Tomorrow 00:00 - 03:00. 160 ProgramTime programTimeTomorrow0000_0300 = ProgramTime.createFromProgram( 161 createDummyProgram(tomorrowAtHourMin(0, 0), TimeUnit.HOURS.toMillis(3))); 162 // Tomorrow 20:00 - Tomorrow 23:00. 163 ProgramTime programTimeTomorrow2000_2300 = ProgramTime.createFromProgram( 164 createDummyProgram(tomorrowAtHourMin(20, 0), TimeUnit.HOURS.toMillis(3))); 165 166 // Check intersection time and commutative law in all cases. 167 int oneHourInSec = hourMinuteToSec(1, 0); 168 assertOverlappedIntervalScore(2 * oneHourInSec, true, programTimeToday2100_2400, 169 programTimeToday2200_0100); 170 assertOverlappedIntervalScore(0, false, programTimeToday2100_2400, 171 programTimeTomorrow0000_0300); 172 assertOverlappedIntervalScore(2 * oneHourInSec, false, programTimeToday2100_2400, 173 programTimeTomorrow2000_2300); 174 assertOverlappedIntervalScore(oneHourInSec, true, programTimeToday2200_0100, 175 programTimeTomorrow0000_0300); 176 assertOverlappedIntervalScore(oneHourInSec, false, programTimeToday2200_0100, 177 programTimeTomorrow2000_2300); 178 assertOverlappedIntervalScore(0, false, programTimeTomorrow0000_0300, 179 programTimeTomorrow2000_2300); 180 } 181 182 public void testGetTimeOfDayInSec() { 183 // Time was set as 00:00:00. So, getTimeOfDay must returns 0 (= 0 * 60 * 60 + 0 * 60 + 0). 184 assertEquals("TimeOfDayInSec", hourMinuteToSec(0, 0), 185 RoutineWatchEvaluator.getTimeOfDayInSec(todayAtHourMin(0, 0))); 186 187 // Time was set as 23:59:59. So, getTimeOfDay must returns 23 * 60 + 60 + 59 * 60 + 59. 188 assertEquals("TimeOfDayInSec", hourMinuteSecondToSec(23, 59, 59), 189 RoutineWatchEvaluator.getTimeOfDayInSec(todayAtHourMinSec(23, 59, 59))); 190 } 191 192 private void assertSplitTextToWords(String text, String... words) { 193 List<String> wordList = RoutineWatchEvaluator.splitTextToWords(text); 194 MoreAsserts.assertContentsInOrder(wordList, words); 195 } 196 197 private void assertMaximumMatchedWordSequenceLength(int expectedLength, String text1, 198 String text2) { 199 List<String> wordList1 = RoutineWatchEvaluator.splitTextToWords(text1); 200 List<String> wordList2 = RoutineWatchEvaluator.splitTextToWords(text2); 201 assertEquals("MaximumMatchedWordSequenceLength", expectedLength, 202 mEvaluator.calculateMaximumMatchedWordSequenceLength(wordList1, wordList2)); 203 assertEquals("MaximumMatchedWordSequenceLength", expectedLength, 204 mEvaluator.calculateMaximumMatchedWordSequenceLength(wordList2, wordList1)); 205 } 206 207 private void assertProgramTime(int expectedWeekDay, int expectedStartTimeOfDayInSec, 208 int expectedEndTimeOfDayInSec, ProgramTime actualProgramTime) { 209 assertEquals("Weekday", expectedWeekDay, actualProgramTime.weekDay); 210 assertEquals("StartTimeOfDayInSec", expectedStartTimeOfDayInSec, 211 actualProgramTime.startTimeOfDayInSec); 212 assertEquals("EndTimeOfDayInSec", expectedEndTimeOfDayInSec, 213 actualProgramTime.endTimeOfDayInSec); 214 } 215 216 private void assertOverlappedIntervalScore(int expectedSeconds, boolean overlappedOnSameDay, 217 ProgramTime t1, ProgramTime t2) { 218 double score = expectedSeconds; 219 if (!overlappedOnSameDay) { 220 score *= RoutineWatchEvaluator.MULTIPLIER_FOR_UNMATCHED_DAY_OF_WEEK; 221 } 222 // Two tests for testing commutative law. 223 assertEquals("OverlappedIntervalScore", score, 224 mEvaluator.calculateOverlappedIntervalScore(t1, t2)); 225 assertEquals("OverlappedIntervalScore", score, 226 mEvaluator.calculateOverlappedIntervalScore(t2, t1)); 227 } 228 229 private int hourMinuteToSec(int hour, int minute) { 230 return hourMinuteSecondToSec(hour, minute, 0); 231 } 232 233 private int hourMinuteSecondToSec(int hour, int minute, int second) { 234 return hour * 60 * 60 + minute * 60 + second; 235 } 236 237 private Calendar todayAtHourMin(int hour, int minute) { 238 return todayAtHourMinSec(hour, minute, 0); 239 } 240 241 private Calendar todayAtHourMinSec(int hour, int minute, int second) { 242 Calendar time = Calendar.getInstance(); 243 time.set(Calendar.HOUR_OF_DAY, hour); 244 time.set(Calendar.MINUTE, minute); 245 time.set(Calendar.SECOND, second); 246 return time; 247 } 248 249 private Calendar tomorrowAtHourMin(int hour, int minute) { 250 Calendar time = todayAtHourMin(hour, minute); 251 time.add(Calendar.DATE, 1); 252 return time; 253 } 254 255 private Program createDummyProgram(Calendar startTime, long programDurationMs) { 256 long startTimeMs = startTime.getTimeInMillis(); 257 258 return new Program.Builder().setStartTimeUtcMillis(startTimeMs) 259 .setEndTimeUtcMillis(startTimeMs + programDurationMs).build(); 260 } 261 262 private static <T> void assertInOrder(T... items) { 263 TreeSet<T> copy = new TreeSet<>(Arrays.asList(items)); 264 MoreAsserts.assertContentsInOrder(copy, items); 265 } 266} 267